diff options
Diffstat (limited to 'asoc')
24 files changed, 5397 insertions, 46 deletions
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c index 6d1887e0..2db0c5b3 100644 --- a/asoc/codecs/bolero/bolero-cdc.c +++ b/asoc/codecs/bolero/bolero-cdc.c @@ -101,8 +101,11 @@ static int __bolero_reg_read(struct bolero_priv *priv, goto ssr_err; } - if (priv->macro_params[VA_MACRO].dev) + if (priv->macro_params[VA_MACRO].dev) { pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev); + if (!bolero_check_core_votes(priv->macro_params[VA_MACRO].dev)) + goto ssr_err; + } if (priv->version < BOLERO_VERSION_2_0) { /* Request Clk before register access */ @@ -149,8 +152,11 @@ static int __bolero_reg_write(struct bolero_priv *priv, ret = -EINVAL; goto ssr_err; } - if (priv->macro_params[VA_MACRO].dev) + if (priv->macro_params[VA_MACRO].dev) { pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev); + if (!bolero_check_core_votes(priv->macro_params[VA_MACRO].dev)) + goto ssr_err; + } if (priv->version < BOLERO_VERSION_2_0) { /* Request Clk before register access */ @@ -828,6 +834,10 @@ static int bolero_ssr_enable(struct device *dev, void *data) priv->component, BOLERO_MACRO_EVT_CLK_RESET, 0x0); } + + if (priv->rsc_clk_cb) + priv->rsc_clk_cb(priv->clk_dev, BOLERO_MACRO_EVT_SSR_GFMUX_UP); + trace_printk("%s: clk count reset\n", __func__); regcache_cache_only(priv->regmap, false); mutex_lock(&priv->clk_lock); diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h index 3144d88b..b37eeae6 100644 --- a/asoc/codecs/bolero/bolero-cdc.h +++ b/asoc/codecs/bolero/bolero-cdc.h @@ -52,7 +52,8 @@ enum { BOLERO_MACRO_EVT_CLK_RESET, BOLERO_MACRO_EVT_REG_WAKE_IRQ, BOLERO_MACRO_EVT_RX_COMPANDER_SOFT_RST, - BOLERO_MACRO_EVT_BCS_CLK_OFF + BOLERO_MACRO_EVT_BCS_CLK_OFF, + BOLERO_MACRO_EVT_SSR_GFMUX_UP, }; enum { diff --git a/asoc/codecs/bolero/bolero-clk-rsc.c b/asoc/codecs/bolero/bolero-clk-rsc.c index daed7e65..9cc9b1ca 100644 --- a/asoc/codecs/bolero/bolero-clk-rsc.c +++ b/asoc/codecs/bolero/bolero-clk-rsc.c @@ -38,6 +38,7 @@ struct bolero_clk_rsc { int reg_seq_en_cnt; int va_tx_clk_cnt; bool dev_up; + bool dev_up_gfmux; u32 num_fs_reg; u32 *fs_gen_seq; int default_clk_id[MAX_CLK]; @@ -65,10 +66,14 @@ static int bolero_clk_rsc_cb(struct device *dev, u16 event) } mutex_lock(&priv->rsc_clk_lock); - if (event == BOLERO_MACRO_EVT_SSR_UP) + if (event == BOLERO_MACRO_EVT_SSR_UP) { priv->dev_up = true; - else if (event == BOLERO_MACRO_EVT_SSR_DOWN) + } else if (event == BOLERO_MACRO_EVT_SSR_DOWN) { priv->dev_up = false; + priv->dev_up_gfmux = false; + } else if (event == BOLERO_MACRO_EVT_SSR_GFMUX_UP) { + priv->dev_up_gfmux = true; + } mutex_unlock(&priv->rsc_clk_lock); return 0; @@ -282,10 +287,12 @@ static int bolero_clk_rsc_mux1_clk_request(struct bolero_clk_rsc *priv, * care in DSP itself */ if (clk_id != VA_CORE_CLK) { - iowrite32(0x1, clk_muxsel); - muxsel = ioread32(clk_muxsel); - trace_printk("%s: muxsel value after enable: %d\n", - __func__, muxsel); + if (priv->dev_up_gfmux) { + iowrite32(0x1, clk_muxsel); + muxsel = ioread32(clk_muxsel); + trace_printk("%s: muxsel value after enable: %d\n", + __func__, muxsel); + } bolero_clk_rsc_mux0_clk_request(priv, default_clk_id, false); @@ -313,10 +320,12 @@ static int bolero_clk_rsc_mux1_clk_request(struct bolero_clk_rsc *priv, * This configuration would be taken * care in DSP itself. */ - iowrite32(0x0, clk_muxsel); - muxsel = ioread32(clk_muxsel); - trace_printk("%s: muxsel value after disable: %d\n", - __func__, muxsel); + if (priv->dev_up_gfmux) { + iowrite32(0x0, clk_muxsel); + muxsel = ioread32(clk_muxsel); + trace_printk("%s: muxsel value after disable: %d\n", + __func__, muxsel); + } } } if (priv->clk[clk_id + NPL_CLK_OFFSET]) @@ -706,6 +715,7 @@ static int bolero_clk_rsc_probe(struct platform_device *pdev) } priv->dev = &pdev->dev; priv->dev_up = true; + priv->dev_up_gfmux = true; mutex_init(&priv->rsc_clk_lock); mutex_init(&priv->fs_gen_lock); dev_set_drvdata(&pdev->dev, priv); diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c index dff74bdf..85d4ae39 100644 --- a/asoc/codecs/bolero/va-macro.c +++ b/asoc/codecs/bolero/va-macro.c @@ -727,14 +727,20 @@ static int va_macro_swrm_clock(void *handle, bool enable) if (va_priv->va_swr_clk_cnt && !va_priv->tx_swr_clk_cnt) { ret = va_macro_tx_va_mclk_enable(va_priv, regmap, VA_MCLK, enable); - if (ret) + if (ret) { + pm_runtime_mark_last_busy(va_priv->dev); + pm_runtime_put_autosuspend(va_priv->dev); goto done; + } va_priv->va_clk_status++; } else { ret = va_macro_tx_va_mclk_enable(va_priv, regmap, TX_MCLK, enable); - if (ret) + if (ret) { + pm_runtime_mark_last_busy(va_priv->dev); + pm_runtime_put_autosuspend(va_priv->dev); goto done; + } va_priv->tx_clk_status++; } pm_runtime_mark_last_busy(va_priv->dev); diff --git a/asoc/codecs/rouleur/Android.mk b/asoc/codecs/rouleur/Android.mk new file mode 100644 index 00000000..4fb57936 --- /dev/null +++ b/asoc/codecs/rouleur/Android.mk @@ -0,0 +1,65 @@ +# 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,bengal),true) +AUDIO_SELECT := CONFIG_SND_SOC_BENGAL=m +endif + +AUDIO_CHIPSET := audio +# Build/Package only in case of supported target +ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET) bengal),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 <chipset>_audio.ko as LOCAL_MODULE. +# This means we need to rename the module to <chipset>_audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=rouleur_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_rouleur.ko +LOCAL_MODULE_KBUILD_NAME := rouleur_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)_rouleur_slave.ko +LOCAL_MODULE_KBUILD_NAME := rouleur_slave_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)_pm2250_spmi.ko +LOCAL_MODULE_KBUILD_NAME := pm2250_spmi_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/asoc/codecs/rouleur/Kbuild b/asoc/codecs/rouleur/Kbuild new file mode 100644 index 00000000..b59bcb11 --- /dev/null +++ b/asoc/codecs/rouleur/Kbuild @@ -0,0 +1,120 @@ +# 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.19 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_BENGAL), y) + include $(AUDIO_ROOT)/config/bengalauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.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) + +############ ROULEUR ############ + +# for ROULEUR Codec +ifdef CONFIG_SND_SOC_ROULEUR + ROULEUR_OBJS += rouleur.o + ROULEUR_OBJS += rouleur-regmap.o + ROULEUR_OBJS += rouleur-tables.o + ROULEUR_OBJS += rouleur-mbhc.o +endif + +ifdef CONFIG_PM2250_SPMI + PM2250_SPMI_OBJS += pm2250_spmi.o +endif + +ifdef CONFIG_SND_SOC_ROULEUR_SLAVE + ROULEUR_SLAVE_OBJS += rouleur_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 + +# Module information used by KBuild framework +obj-$(CONFIG_SND_SOC_ROULEUR) += rouleur_dlkm.o +rouleur_dlkm-y := $(ROULEUR_OBJS) + +obj-$(CONFIG_SND_SOC_ROULEUR_SLAVE) += rouleur_slave_dlkm.o +rouleur_slave_dlkm-y := $(ROULEUR_SLAVE_OBJS) + +obj-$(CONFIG_PM2250_SPMI) += pm2250_spmi_dlkm.o +pm2250_spmi_dlkm-y := $(PM2250_SPMI_OBJS) + +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/asoc/codecs/rouleur/internal.h b/asoc/codecs/rouleur/internal.h new file mode 100644 index 00000000..7104685d --- /dev/null +++ b/asoc/codecs/rouleur/internal.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _ROULEUR_INTERNAL_H +#define _ROULEUR_INTERNAL_H + +#include <asoc/wcd-clsh.h> +#include <asoc/wcd-mbhc-v2.h> +#include <asoc/wcd-irq.h> +#include "rouleur-mbhc.h" + +#define ROULEUR_MAX_MICBIAS 3 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1600 + v * 50) +#define MAX_PORT 8 +#define MAX_CH_PER_PORT 8 + +extern struct regmap_config rouleur_regmap_config; + +struct codec_port_info { + u32 slave_port_type; + u32 master_port_type; + u32 ch_mask; + u32 num_ch; + u32 ch_rate; +}; + +struct rouleur_priv { + struct device *dev; + + int variant; + struct snd_soc_component *component; + struct device_node *spmi_np; + struct regmap *regmap; + + struct swr_device *rx_swr_dev; + struct swr_device *tx_swr_dev; + + s32 micb_ref[ROULEUR_MAX_MICBIAS]; + s32 pullup_ref[ROULEUR_MAX_MICBIAS]; + + struct fw_info *fw_data; + + struct mutex micb_lock; + s32 dmic_0_1_clk_cnt; + /* mbhc module */ + struct rouleur_mbhc *mbhc; + + 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; + struct device *spmi_dev; + int reset_reg; + int mbias_cnt; + struct mutex rx_clk_lock; + struct mutex main_bias_lock; +}; + +struct rouleur_micbias_setting { + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; +}; + +struct rouleur_pdata { + struct device_node *spmi_np; + struct device_node *rx_slave; + struct device_node *tx_slave; + struct rouleur_micbias_setting micbias; + + struct cdc_regulator *regulator; + int num_supplies; + int reset_reg; +}; + +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 */ + WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST, + WCD_BOLERO_EVT_BCS_CLK_OFF, +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET = 0, + ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET, + ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET, + ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + ROULEUR_IRQ_MBHC_SW_DET, + ROULEUR_IRQ_HPHR_OCP_INT, + ROULEUR_IRQ_HPHR_CNP_INT, + ROULEUR_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + ROULEUR_IRQ_HPHL_CNP_INT, + ROULEUR_IRQ_EAR_CNP_INT, + ROULEUR_IRQ_EAR_OCP_INT, + ROULEUR_IRQ_LO_CNP_INT, + ROULEUR_IRQ_LO_OCP_INT, + ROULEUR_IRQ_HPHL_PDM_WD_INT, + ROULEUR_IRQ_HPHR_PDM_WD_INT, + ROULEUR_IRQ_RESERVED_0, + + /* INTR_CTRL_INT_MASK_2 */ + ROULEUR_IRQ_RESERVED_1, + ROULEUR_IRQ_RESERVED_2, + ROULEUR_IRQ_HPHL_SURGE_DET_INT, + ROULEUR_IRQ_HPHR_SURGE_DET_INT, + ROULEUR_NUM_IRQS, +}; + +extern void rouleur_disable_bcs_before_slow_insert( + struct snd_soc_component *component, + bool bcs_disable); +extern struct rouleur_mbhc *rouleur_soc_get_mbhc( + struct snd_soc_component *component); +extern int rouleur_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int volt, int micb_num); +extern int rouleur_get_micb_vout_ctl_val(u32 micb_mv); +extern int rouleur_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm); +#endif diff --git a/asoc/codecs/rouleur/pm2250-spmi.h b/asoc/codecs/rouleur/pm2250-spmi.h new file mode 100644 index 00000000..87c913d4 --- /dev/null +++ b/asoc/codecs/rouleur/pm2250-spmi.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _PM2250_SPMI_H +#define _PM2250_SPMI_H + +#ifdef CONFIG_PM2250_SPMI +int pm2250_spmi_write(struct device *dev, int reg, int value); +#else +int pm2250_spmi_write(struct device *dev, int reg, int value) +{ + return 0; +} +#endif /* CONFIG_PM2250_SPMI */ + +#endif diff --git a/asoc/codecs/rouleur/pm2250_spmi.c b/asoc/codecs/rouleur/pm2250_spmi.c new file mode 100644 index 00000000..1e5f70d1 --- /dev/null +++ b/asoc/codecs/rouleur/pm2250_spmi.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/** + * @regmap: regmap used to access PMIC registers + */ +struct pm2250_spmi { + struct regmap *regmap; +}; + +static const struct of_device_id pm2250_id_table[] = { + { .compatible = "qcom,pm2250-spmi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pm2250_id_table); + +int pm2250_spmi_write(struct device *dev, int reg, int value) +{ + int rc; + struct pm2250_spmi *spmi_dd; + + if (!of_device_is_compatible(dev->of_node, "qcom,pm2250-spmi")) { + pr_err("%s: Device node is invalid\n", __func__); + return -EINVAL; + } + + spmi_dd = dev_get_drvdata(dev); + if (!spmi_dd) + return -EINVAL; + + rc = regmap_write(spmi_dd->regmap, reg, value); + if (rc) + dev_err(dev, "%s: Write to PMIC register failed\n", __func__); + + return rc; +} +EXPORT_SYMBOL(pm2250_spmi_write); + +static int pm2250_spmi_probe(struct platform_device *pdev) +{ + struct pm2250_spmi *spmi_dd; + const struct of_device_id *match; + + match = of_match_node(pm2250_id_table, pdev->dev.of_node); + if (!match) + return -ENXIO; + + spmi_dd = devm_kzalloc(&pdev->dev, sizeof(*spmi_dd), GFP_KERNEL); + if (spmi_dd == NULL) + return -ENOMEM; + + spmi_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!spmi_dd->regmap) { + dev_err(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } + + platform_set_drvdata(pdev, spmi_dd); + + dev_dbg(&pdev->dev, "Probe success !!\n"); + + return 0; +} + +static int pm2250_spmi_remove(struct platform_device *pdev) +{ + of_platform_depopulate(&pdev->dev); + return 0; +} + +static struct platform_driver pm2250_spmi_driver = { + .probe = pm2250_spmi_probe, + .remove = pm2250_spmi_remove, + .driver = { + .name = "pm2250-spmi", + .of_match_table = pm2250_id_table, + }, +}; +module_platform_driver(pm2250_spmi_driver); + +MODULE_ALIAS("platform:pm2250-spmi"); +MODULE_DESCRIPTION("PMIC SPMI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/rouleur/rouleur-mbhc.c b/asoc/codecs/rouleur/rouleur-mbhc.c new file mode 100644 index 00000000..3e3ef511 --- /dev/null +++ b/asoc/codecs/rouleur/rouleur-mbhc.c @@ -0,0 +1,1155 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include "rouleur-registers.h" +#include <asoc/wcdcal-hwdep.h> +#include <asoc/wcd-mbhc-v2-api.h> +#include "internal.h" + +#define ROULEUR_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define ROULEUR_ZDET_VAL_32 32000 +#define ROULEUR_ZDET_VAL_400 400000 +#define ROULEUR_ZDET_VAL_1200 1200000 +#define ROULEUR_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define ROULEUR_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define ROULEUR_ZDET_NUM_MEASUREMENTS 900 +#define ROULEUR_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define ROULEUR_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define ROULEUR_MBHC_ZDET_CONST (86 * 16384) +#define ROULEUR_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", + ROULEUR_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + ROULEUR_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + ROULEUR_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + ROULEUR_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + ROULEUR_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + ROULEUR_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + ROULEUR_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + ROULEUR_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + ROULEUR_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + ROULEUR_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + ROULEUR_ANA_MBHC_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + ROULEUR_ANA_MBHC_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + ROULEUR_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + SND_SOC_NOPM, 0x00, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + ROULEUR_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + ROULEUR_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + ROULEUR_ANA_MICBIAS_MICB_1_2_EN, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + SND_SOC_NOPM, 0x00, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + ROULEUR_ANA_HPHPA_CNP_CTL_2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + ROULEUR_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", + SND_SOC_NOPM, 0x00, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + ROULEUR_ANA_MBHC_FSM_STATUS, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + ROULEUR_ANA_MBHC_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + ROULEUR_ANA_MBHC_FSM_STATUS, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + SND_SOC_NOPM, 0x00, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + SND_SOC_NOPM, 0x00, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x02, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + ROULEUR_DIG_SWR_INTR_STATUS_0, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + ROULEUR_DIG_SWR_INTR_STATUS_0, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN", + ROULEUR_ANA_MBHC_CTL_1, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", ROULEUR_ANA_MBHC_FSM_STATUS, + 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", ROULEUR_ANA_MBHC_FSM_STATUS, + 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", ROULEUR_ANA_MBHC_ADC_RESULT, + 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT", + ROULEUR_ANA_MICBIAS_LDO_1_SETTING, 0xF8, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE", + ROULEUR_ANA_MBHC_CTL_1, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE", + ROULEUR_ANA_MBHC_CTL_1, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN", + ROULEUR_ANA_MBHC_ZDET, 0x02, 1, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = ROULEUR_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = ROULEUR_IRQ_HPHL_OCP_INT, + .hph_right_ocp = ROULEUR_IRQ_HPHR_OCP_INT, +}; + +struct rouleur_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int rouleur_mbhc_request_irq(struct snd_soc_component *component, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + + return wcd_request_irq(&rouleur->irq_info, irq, name, handler, data); +} + +static void rouleur_mbhc_irq_control(struct snd_soc_component *component, + int irq, bool enable) +{ + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + + if (enable) + wcd_enable_irq(&rouleur->irq_info, irq); + else + wcd_disable_irq(&rouleur->irq_info, irq); +} + +static int rouleur_mbhc_free_irq(struct snd_soc_component *component, + int irq, void *data) +{ + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + + wcd_free_irq(&rouleur->irq_info, irq, data); + + return 0; +} + +static void rouleur_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + if (enable) + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_1, + 0x80, 0x80); + else + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_1, + 0x80, 0x00); +} + +static int rouleur_mbhc_btn_to_num(struct snd_soc_component *component) +{ + return snd_soc_component_read32(component, ROULEUR_ANA_MBHC_RESULT_3) & + 0x7; +} + +static void rouleur_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void rouleur_mbhc_program_btn_thr(struct snd_soc_component *component, + 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(component->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_component_update_bits(component, + ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1 + i, + 0xFC, vth << 2); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool rouleur_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_component *component = mbhc->component; + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + + rouleur->wakeup((void *)rouleur, lock); + return true; +} + +static int rouleur_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct rouleur_mbhc *rouleur_mbhc; + + rouleur_mbhc = container_of(mbhc, struct rouleur_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&rouleur_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &rouleur_mbhc->notifier, nblock); +} + +static bool rouleur_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val = 0; + + if (micb_num == MIC_BIAS_2) { + val = ((snd_soc_component_read32(mbhc->component, + ROULEUR_ANA_MICBIAS_MICB_1_2_EN) & 0x04) + >> 2); + if (val == 0x01) + return true; + } + return false; +} + +static bool rouleur_mbhc_hph_pa_on_status(struct snd_soc_component *component) +{ + return (snd_soc_component_read32(component, ROULEUR_ANA_HPHPA_PA_STATUS) + & 0xFF) ? true : false; +} + +static void rouleur_mbhc_hph_l_pull_up_control( + struct snd_soc_component *component, + int 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(component->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, + 0xC0, pull_up_cur); +} + +static int rouleur_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + int ret = 0; + + ret = rouleur_micbias_control(component, micb_num, req, false); + + return ret; +} + +static void rouleur_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBHC_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBHC_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBHC_MICB2_RAMP, + 0x80, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBHC_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *rouleur_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct rouleur_mbhc *rouleur_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_component *component = mbhc->component; + + rouleur_mbhc = container_of(mbhc, struct rouleur_mbhc, wcd_mbhc); + + if (!component) { + pr_err("%s: NULL component pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(rouleur_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(component->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int rouleur_mbhc_micb_ctrl_threshold_mic( + struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct rouleur_pdata *pdata = dev_get_platdata(component->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 = rouleur_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void rouleur_mbhc_get_result_params(struct rouleur_priv *rouleur, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val = 0, val1 = 0; + s16 c1 = 0; + s32 x1 = 0, d1 = 0; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < ROULEUR_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = ROULEUR_MBHC_GET_X1(val); + c1 = ROULEUR_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(rouleur->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 = (ROULEUR_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = ROULEUR_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(rouleur->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(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_1, &val); + regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_2, &val1); + val = val << 0x8; + val |= val1; + x1 = ROULEUR_MBHC_GET_X1(val); + i++; + if (i == ROULEUR_ZDET_NUM_MEASUREMENTS) + break; + } +} + +#if 0 +static void rouleur_mbhc_zdet_ramp(struct snd_soc_component *component, + struct rouleur_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + int32_t zdet = 0; + + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL, + 0x70, zdet_param->ldo_ctl << 4); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL, + 0x0F, zdet_param->noff); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(rouleur->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + rouleur_mbhc_get_result_params(rouleur, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(rouleur->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + rouleur_mbhc_get_result_params(rouleur, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void rouleur_wcd_mbhc_qfuse_cal( + struct snd_soc_component *component, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (ROULEUR_ZDET_VAL_400/1000)) + q1 = snd_soc_component_read32(component, + ROULEUR_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read32(component, + ROULEUR_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 rouleur_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_component *component = mbhc->component; + struct rouleur_priv *rouleur = dev_get_drvdata(component->dev); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct rouleur_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 rouleur_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_component_read32(component, ROULEUR_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read32(component, ROULEUR_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read32(component, ROULEUR_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read32(component, ROULEUR_MBHC_CTL_CLK); + reg4 = snd_soc_component_read32(component, + ROULEUR_ANA_MBHC_ZDET_ANA_CTL); + + if (snd_soc_component_read32(component, ROULEUR_ANA_MBHC_ELECT) & + 0x80) { + is_fsm_disable = true; + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_MECH, 0x01, 0x00); + + /* Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + regmap_update_bits(rouleur->regmap, + ROULEUR_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00); + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + + if (!ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < ROULEUR_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > ROULEUR_ZDET_VAL_400) && + (z1L <= ROULEUR_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > ROULEUR_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == ROULEUR_ZDET_FLOATING_IMPEDANCE) || + (z1L > ROULEUR_ZDET_VAL_100K)) { + *zl = ROULEUR_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + rouleur_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + if (ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > ROULEUR_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != ROULEUR_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < ROULEUR_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > ROULEUR_ZDET_VAL_400) && + (z1R <= ROULEUR_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > ROULEUR_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, + &z1R, d1); + } +right_ch_impedance: + if ((z1R == ROULEUR_ZDET_FLOATING_IMPEDANCE) || + (z1R > ROULEUR_ZDET_VAL_100K)) { + *zr = ROULEUR_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + rouleur_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == ROULEUR_ZDET_FLOATING_IMPEDANCE) && + (*zr == ROULEUR_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == ROULEUR_ZDET_FLOATING_IMPEDANCE) || + (*zr == ROULEUR_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(component->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_component_update_bits(component, ROULEUR_HPH_R_ATEST, + 0x02, 0x02); + snd_soc_component_update_bits(component, ROULEUR_HPH_PA_CTL2, + 0x40, 0x01); + if (*zl < (ROULEUR_ZDET_VAL_32/1000)) + rouleur_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, + NULL, d1); + else + rouleur_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, + NULL, d1); + snd_soc_component_update_bits(component, ROULEUR_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_component_update_bits(component, ROULEUR_HPH_R_ATEST, + 0x02, 0x00); + z1Ls /= 1000; + rouleur_wcd_mbhc_qfuse_cal(component, &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(component->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + + /* Enable surge protection again after impedance detection */ + regmap_update_bits(rouleur->regmap, + ROULEUR_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); +zdet_complete: + snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, ROULEUR_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(rouleur->regmap, + ROULEUR_ANA_MBHC_ELECT, 0x80, 0x80); +} +#endif + +static void rouleur_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void rouleur_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x30, 0x10); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x0C, 0x04); + } else { + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x30, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x0C, 0x00); + } +} + +static void rouleur_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_component *component = mbhc->component; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, mbhc->moist_rref << 2); +} + +static void rouleur_mbhc_moisture_detect_en(struct wcd_mbhc *mbhc, bool enable) +{ + struct snd_soc_component *component = mbhc->component; + + if (enable) + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, mbhc->moist_rref << 2); + else + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, R_OFF << 2); +} + +static bool rouleur_mbhc_get_moisture_status(struct wcd_mbhc *mbhc) +{ + struct snd_soc_component *component = mbhc->component; + bool ret = false; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, R_OFF << 2); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2, + 0x0C, R_OFF << 2); + goto done; + } + + /* If moisture_en is already enabled, then skip to plug type + * detection. + */ + if ((snd_soc_component_read32(component, ROULEUR_ANA_MBHC_CTL_2) & + 0x0C)) + goto done; + + rouleur_mbhc_moisture_detect_en(mbhc, true); + /* Read moisture comparator status */ + ret = ((snd_soc_component_read32(component, ROULEUR_ANA_MBHC_FSM_STATUS) + & 0x20) ? 0 : 1); + +done: + return ret; + +} + +static void rouleur_mbhc_bcs_enable(struct wcd_mbhc *mbhc, + bool bcs_enable) +{ + if (bcs_enable) + rouleur_disable_bcs_before_slow_insert(mbhc->component, false); + else + rouleur_disable_bcs_before_slow_insert(mbhc->component, true); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = rouleur_mbhc_request_irq, + .irq_control = rouleur_mbhc_irq_control, + .free_irq = rouleur_mbhc_free_irq, + .clk_setup = rouleur_mbhc_clk_setup, + .map_btn_code_to_num = rouleur_mbhc_btn_to_num, + .mbhc_bias = rouleur_mbhc_mbhc_bias_control, + .set_btn_thr = rouleur_mbhc_program_btn_thr, + .lock_sleep = rouleur_mbhc_lock_sleep, + .register_notifier = rouleur_mbhc_register_notifier, + .micbias_enable_status = rouleur_mbhc_micb_en_status, + .hph_pa_on_status = rouleur_mbhc_hph_pa_on_status, + .hph_pull_up_control = rouleur_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = rouleur_mbhc_request_micbias, + .mbhc_micb_ramp_control = rouleur_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = rouleur_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = rouleur_mbhc_micb_ctrl_threshold_mic, + //.compute_impedance = rouleur_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = rouleur_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = rouleur_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = rouleur_mbhc_moisture_config, + .mbhc_get_moisture_status = rouleur_mbhc_get_moisture_status, + .mbhc_moisture_detect_en = rouleur_mbhc_moisture_detect_en, + .bcs_enable = rouleur_mbhc_bcs_enable, +}; + +static int rouleur_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rouleur_mbhc *rouleur_mbhc = rouleur_soc_get_mbhc(component); + struct wcd_mbhc *mbhc; + + if (!rouleur_mbhc) { + dev_err(component->dev, "%s: mbhc not initialized!\n", + __func__); + return -EINVAL; + } + + mbhc = &rouleur_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(component->dev, "%s: hph_type = %u\n", __func__, + mbhc->hph_type); + + return 0; +} + +static int rouleur_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl = 0, zr = 0; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rouleur_mbhc *rouleur_mbhc = rouleur_soc_get_mbhc(component); + + if (!rouleur_mbhc) { + dev_err(component->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(&rouleur_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(component->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, + rouleur_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + rouleur_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + rouleur_hph_impedance_get, NULL), +}; + +/* + * rouleur_mbhc_get_impedance: get impedance of headphone + * left and right channels + * @rouleur_mbhc: handle to struct rouleur_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 rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (!rouleur_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(&rouleur_mbhc->wcd_mbhc, zl, zr); +} +EXPORT_SYMBOL(rouleur_mbhc_get_impedance); + +/* + * rouleur_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @component: handle to snd_soc_component * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int rouleur_mbhc_hs_detect(struct snd_soc_component *component, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct rouleur_priv *rouleur = NULL; + struct rouleur_mbhc *rouleur_mbhc = NULL; + + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return -EINVAL; + } + + rouleur = snd_soc_component_get_drvdata(component); + if (!rouleur) { + pr_err("%s: rouleur is NULL\n", __func__); + return -EINVAL; + } + + rouleur_mbhc = rouleur->mbhc; + if (!rouleur_mbhc) { + dev_err(component->dev, "%s: mbhc not initialized!\n", + __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&rouleur_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(rouleur_mbhc_hs_detect); + +/* + * rouleur_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @component: handle to snd_soc_component * + */ +void rouleur_mbhc_hs_detect_exit(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = NULL; + struct rouleur_mbhc *rouleur_mbhc = NULL; + + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return; + } + + rouleur = snd_soc_component_get_drvdata(component); + if (!rouleur) { + pr_err("%s: rouleur is NULL\n", __func__); + return; + } + + rouleur_mbhc = rouleur->mbhc; + if (!rouleur_mbhc) { + dev_err(component->dev, "%s: mbhc not initialized!\n", + __func__); + return; + } + wcd_mbhc_stop(&rouleur_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(rouleur_mbhc_hs_detect_exit); + +/* + * rouleur_mbhc_ssr_down: stop mbhc during + * rouleur subsystem restart + * @mbhc: pointer to rouleur_mbhc structure + * @component: handle to snd_soc_component * + */ +void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component) +{ + struct wcd_mbhc *wcd_mbhc = NULL; + + if (!mbhc || !component) + return; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + dev_err(component->dev, "%s: wcd_mbhc is NULL\n", __func__); + return; + } + + rouleur_mbhc_hs_detect_exit(component); + wcd_mbhc_deinit(wcd_mbhc); +} +EXPORT_SYMBOL(rouleur_mbhc_ssr_down); + +/* + * rouleur_mbhc_post_ssr_init: initialize mbhc for + * rouleur post subsystem restart + * @mbhc: poniter to rouleur_mbhc structure + * @component: handle to snd_soc_component * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component) +{ + int ret = 0; + struct wcd_mbhc *wcd_mbhc = NULL; + + if (!mbhc || !component) + return -EINVAL; + + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + return -EINVAL; + } + + snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH, + 0x20, 0x20); + ret = wcd_mbhc_init(wcd_mbhc, component, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, ROULEUR_ZDET_SUPPORTED); + if (ret) + dev_err(component->dev, "%s: mbhc initialization failed\n", + __func__); + + return ret; +} +EXPORT_SYMBOL(rouleur_mbhc_post_ssr_init); + +/* + * rouleur_mbhc_init: initialize mbhc for rouleur + * @mbhc: poniter to rouleur_mbhc struct pointer to store the configs + * @component: handle to snd_soc_component * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int rouleur_mbhc_init(struct rouleur_mbhc **mbhc, + struct snd_soc_component *component, + struct fw_info *fw_data) +{ + struct rouleur_mbhc *rouleur_mbhc = NULL; + struct wcd_mbhc *wcd_mbhc = NULL; + struct rouleur_pdata *pdata; + int ret = 0; + + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return -EINVAL; + } + + rouleur_mbhc = devm_kzalloc(component->dev, sizeof(struct rouleur_mbhc), + GFP_KERNEL); + if (!rouleur_mbhc) + return -ENOMEM; + + rouleur_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&rouleur_mbhc->notifier); + wcd_mbhc = &rouleur_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; + + pdata = dev_get_platdata(component->dev); + if (!pdata) { + dev_err(component->dev, "%s: pdata pointer is NULL\n", + __func__); + ret = -EINVAL; + goto err; + } + wcd_mbhc->micb_mv = pdata->micbias.micb2_mv; + + ret = wcd_mbhc_init(wcd_mbhc, component, &mbhc_cb, + &intr_ids, wcd_mbhc_registers, + ROULEUR_ZDET_SUPPORTED); + if (ret) { + dev_err(component->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + (*mbhc) = rouleur_mbhc; + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +err: + devm_kfree(component->dev, rouleur_mbhc); + return ret; +} +EXPORT_SYMBOL(rouleur_mbhc_init); + +/* + * rouleur_mbhc_deinit: deinitialize mbhc for rouleur + * @component: handle to snd_soc_component * + */ +void rouleur_mbhc_deinit(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur; + struct rouleur_mbhc *rouleur_mbhc; + + if (!component) { + pr_err("%s: component is NULL\n", __func__); + return; + } + + rouleur = snd_soc_component_get_drvdata(component); + if (!rouleur) { + pr_err("%s: rouleur is NULL\n", __func__); + return; + } + + rouleur_mbhc = rouleur->mbhc; + if (rouleur_mbhc) { + wcd_mbhc_deinit(&rouleur_mbhc->wcd_mbhc); + devm_kfree(component->dev, rouleur_mbhc); + } +} +EXPORT_SYMBOL(rouleur_mbhc_deinit); diff --git a/asoc/codecs/rouleur/rouleur-mbhc.h b/asoc/codecs/rouleur/rouleur-mbhc.h new file mode 100644 index 00000000..40bef376 --- /dev/null +++ b/asoc/codecs/rouleur/rouleur-mbhc.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ +#ifndef __ROULEUR_MBHC_H__ +#define __ROULEUR_MBHC_H__ +#include <asoc/wcd-mbhc-v2.h> + +struct rouleur_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct fw_info *fw_data; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_ROULEUR) +extern int rouleur_mbhc_init(struct rouleur_mbhc **mbhc, + struct snd_soc_component *component, + struct fw_info *fw_data); +extern void rouleur_mbhc_hs_detect_exit(struct snd_soc_component *component); +extern int rouleur_mbhc_hs_detect(struct snd_soc_component *component, + struct wcd_mbhc_config *mbhc_cfg); +extern void rouleur_mbhc_deinit(struct snd_soc_component *component); +extern int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component); +extern void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component); +extern int rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc, + uint32_t *zl, uint32_t *zr); +#else +static inline int rouleur_mbhc_init(struct rouleur_mbhc **mbhc, + struct snd_soc_component *component, + struct fw_info *fw_data) +{ + return 0; +} +static inline void rouleur_mbhc_hs_detect_exit( + struct snd_soc_component *component) +{ +} +static inline int rouleur_mbhc_hs_detect(struct snd_soc_component *component, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void rouleur_mbhc_deinit(struct snd_soc_component *component) +{ +} +static inline int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component) +{ + return 0; +} +static inline void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc, + struct snd_soc_component *component) +{ +} +static inline int rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (zl) + *zl = 0; + if (zr) + *zr = 0; + return -EINVAL; +} +#endif + +#endif /* __ROULEUR_MBHC_H__ */ diff --git a/asoc/codecs/rouleur/rouleur-registers.h b/asoc/codecs/rouleur/rouleur-registers.h new file mode 100644 index 00000000..ff3934bb --- /dev/null +++ b/asoc/codecs/rouleur/rouleur-registers.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _ROULEUR_REGISTERS_H +#define _ROULEUR_REGISTERS_H + +#define ROULEUR_ANA_BASE_ADDR 0x3000 +#define ROULEUR_DIG_BASE_ADDR 0x3400 + +#define ROULEUR_REG(reg) ((reg > ROULEUR_DIG_BASE_ADDR) ? \ + (reg - ROULEUR_DIG_BASE_ADDR) : \ + (reg - ROULEUR_ANA_BASE_ADDR)) + +enum { + REG_NO_ACCESS, + RD_REG, + WR_REG, + RD_WR_REG +}; + +#define ROULEUR_ANA_MICBIAS_MICB_1_2_EN (ROULEUR_ANA_BASE_ADDR+0x040) +#define ROULEUR_ANA_MICBIAS_MICB_3_EN (ROULEUR_ANA_BASE_ADDR+0x041) +#define ROULEUR_ANA_MICBIAS_LDO_1_SETTING (ROULEUR_ANA_BASE_ADDR+0x042) +#define ROULEUR_ANA_MICBIAS_LDO_1_CTRL (ROULEUR_ANA_BASE_ADDR+0x043) +#define ROULEUR_ANA_TX_AMIC1 (ROULEUR_ANA_BASE_ADDR+0x047) +#define ROULEUR_ANA_TX_AMIC2 (ROULEUR_ANA_BASE_ADDR+0x048) +#define ROULEUR_ANA_MBHC_MECH (ROULEUR_ANA_BASE_ADDR+0x05A) +#define ROULEUR_ANA_MBHC_ELECT (ROULEUR_ANA_BASE_ADDR+0x05B) +#define ROULEUR_ANA_MBHC_ZDET (ROULEUR_ANA_BASE_ADDR+0x05C) +#define ROULEUR_ANA_MBHC_RESULT_1 (ROULEUR_ANA_BASE_ADDR+0x05D) +#define ROULEUR_ANA_MBHC_RESULT_2 (ROULEUR_ANA_BASE_ADDR+0x05E) +#define ROULEUR_ANA_MBHC_RESULT_3 (ROULEUR_ANA_BASE_ADDR+0x05F) +#define ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1 (ROULEUR_ANA_BASE_ADDR+0x060) +#define ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2 (ROULEUR_ANA_BASE_ADDR+0x061) +#define ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3 (ROULEUR_ANA_BASE_ADDR+0x062) +#define ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400 (ROULEUR_ANA_BASE_ADDR+0x063) +#define ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400 (ROULEUR_ANA_BASE_ADDR+0x064) +#define ROULEUR_ANA_MBHC_MICB2_RAMP (ROULEUR_ANA_BASE_ADDR+0x065) +#define ROULEUR_ANA_MBHC_CTL_1 (ROULEUR_ANA_BASE_ADDR+0x066) +#define ROULEUR_ANA_MBHC_CTL_2 (ROULEUR_ANA_BASE_ADDR+0x067) +#define ROULEUR_ANA_MBHC_PLUG_DETECT_CTL (ROULEUR_ANA_BASE_ADDR+0x068) +#define ROULEUR_ANA_MBHC_ZDET_ANA_CTL (ROULEUR_ANA_BASE_ADDR+0x069) +#define ROULEUR_ANA_MBHC_ZDET_RAMP_CTL (ROULEUR_ANA_BASE_ADDR+0x06A) +#define ROULEUR_ANA_MBHC_FSM_STATUS (ROULEUR_ANA_BASE_ADDR+0x06B) +#define ROULEUR_ANA_MBHC_ADC_RESULT (ROULEUR_ANA_BASE_ADDR+0x06C) +#define ROULEUR_ANA_MBHC_MCLK (ROULEUR_ANA_BASE_ADDR+0x06D) +#define ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT (ROULEUR_ANA_BASE_ADDR+0x072) +#define ROULEUR_ANA_NCP_EN (ROULEUR_ANA_BASE_ADDR+0x077) +#define ROULEUR_ANA_HPHPA_CNP_CTL_2 (ROULEUR_ANA_BASE_ADDR+0x084) +#define ROULEUR_ANA_HPHPA_PA_STATUS (ROULEUR_ANA_BASE_ADDR+0x087) +#define ROULEUR_ANA_HPHPA_FSM_CLK (ROULEUR_ANA_BASE_ADDR+0x088) +#define ROULEUR_ANA_HPHPA_L_GAIN (ROULEUR_ANA_BASE_ADDR+0x08B) +#define ROULEUR_ANA_HPHPA_R_GAIN (ROULEUR_ANA_BASE_ADDR+0x08C) +#define ROULEUR_ANA_HPHPA_SPARE_CTL (ROULEUR_ANA_BASE_ADDR+0x08E) +#define ROULEUR_ANA_SURGE_EN (ROULEUR_ANA_BASE_ADDR+0x097) +#define ROULEUR_ANA_COMBOPA_CTL (ROULEUR_ANA_BASE_ADDR+0x09B) +#define ROULEUR_ANA_RXLDO_CTL (ROULEUR_ANA_BASE_ADDR+0x0B2) +#define ROULEUR_ANA_MBIAS_EN (ROULEUR_ANA_BASE_ADDR+0x0B4) + +#define ROULEUR_DIG_SWR_CHIP_ID0 (ROULEUR_DIG_BASE_ADDR+0x001) +#define ROULEUR_DIG_SWR_CHIP_ID1 (ROULEUR_DIG_BASE_ADDR+0x002) +#define ROULEUR_DIG_SWR_CHIP_ID2 (ROULEUR_DIG_BASE_ADDR+0x003) +#define ROULEUR_DIG_SWR_CHIP_ID3 (ROULEUR_DIG_BASE_ADDR+0x004) +#define ROULEUR_DIG_SWR_SWR_TX_CLK_RATE (ROULEUR_DIG_BASE_ADDR+0x040) +#define ROULEUR_DIG_SWR_CDC_RST_CTL (ROULEUR_DIG_BASE_ADDR+0x041) +#define ROULEUR_DIG_SWR_TOP_CLK_CFG (ROULEUR_DIG_BASE_ADDR+0x042) +#define ROULEUR_DIG_SWR_CDC_RX_CLK_CTL (ROULEUR_DIG_BASE_ADDR+0x043) +#define ROULEUR_DIG_SWR_CDC_TX_CLK_CTL (ROULEUR_DIG_BASE_ADDR+0x044) +#define ROULEUR_DIG_SWR_SWR_RST_EN (ROULEUR_DIG_BASE_ADDR+0x045) +#define ROULEUR_DIG_SWR_CDC_RX_RST (ROULEUR_DIG_BASE_ADDR+0x047) +#define ROULEUR_DIG_SWR_CDC_RX0_CTL (ROULEUR_DIG_BASE_ADDR+0x048) +#define ROULEUR_DIG_SWR_CDC_RX1_CTL (ROULEUR_DIG_BASE_ADDR+0x049) +#define ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1 (ROULEUR_DIG_BASE_ADDR+0x04B) +#define ROULEUR_DIG_SWR_CDC_COMP_CTL_0 (ROULEUR_DIG_BASE_ADDR+0x04F) +#define ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL (ROULEUR_DIG_BASE_ADDR+0x052) +#define ROULEUR_DIG_SWR_CDC_RX_GAIN_0 (ROULEUR_DIG_BASE_ADDR+0x053) +#define ROULEUR_DIG_SWR_CDC_RX_GAIN_1 (ROULEUR_DIG_BASE_ADDR+0x054) +#define ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL (ROULEUR_DIG_BASE_ADDR+0x057) +#define ROULEUR_DIG_SWR_CDC_TX0_CTL (ROULEUR_DIG_BASE_ADDR+0x060) +#define ROULEUR_DIG_SWR_CDC_TX1_CTL (ROULEUR_DIG_BASE_ADDR+0x061) +#define ROULEUR_DIG_SWR_CDC_TX_RST (ROULEUR_DIG_BASE_ADDR+0x063) +#define ROULEUR_DIG_SWR_CDC_REQ0_CTL (ROULEUR_DIG_BASE_ADDR+0x064) +#define ROULEUR_DIG_SWR_CDC_REQ1_CTL (ROULEUR_DIG_BASE_ADDR+0x065) +#define ROULEUR_DIG_SWR_CDC_RST (ROULEUR_DIG_BASE_ADDR+0x067) +#define ROULEUR_DIG_SWR_CDC_AMIC_CTL (ROULEUR_DIG_BASE_ADDR+0x06A) +#define ROULEUR_DIG_SWR_CDC_DMIC_CTL (ROULEUR_DIG_BASE_ADDR+0x06B) +#define ROULEUR_DIG_SWR_CDC_DMIC1_CTL (ROULEUR_DIG_BASE_ADDR+0x06C) +#define ROULEUR_DIG_SWR_CDC_DMIC1_RATE (ROULEUR_DIG_BASE_ADDR+0x06D) +#define ROULEUR_DIG_SWR_PDM_WD_CTL0 (ROULEUR_DIG_BASE_ADDR+0x070) +#define ROULEUR_DIG_SWR_PDM_WD_CTL1 (ROULEUR_DIG_BASE_ADDR+0x071) +#define ROULEUR_DIG_SWR_INTR_MODE (ROULEUR_DIG_BASE_ADDR+0x080) +#define ROULEUR_DIG_SWR_INTR_MASK_0 (ROULEUR_DIG_BASE_ADDR+0x081) +#define ROULEUR_DIG_SWR_INTR_MASK_1 (ROULEUR_DIG_BASE_ADDR+0x082) +#define ROULEUR_DIG_SWR_INTR_MASK_2 (ROULEUR_DIG_BASE_ADDR+0x083) +#define ROULEUR_DIG_SWR_INTR_STATUS_0 (ROULEUR_DIG_BASE_ADDR+0x084) +#define ROULEUR_DIG_SWR_INTR_STATUS_1 (ROULEUR_DIG_BASE_ADDR+0x085) +#define ROULEUR_DIG_SWR_INTR_STATUS_2 (ROULEUR_DIG_BASE_ADDR+0x086) +#define ROULEUR_DIG_SWR_INTR_CLEAR_0 (ROULEUR_DIG_BASE_ADDR+0x087) +#define ROULEUR_DIG_SWR_INTR_CLEAR_1 (ROULEUR_DIG_BASE_ADDR+0x088) +#define ROULEUR_DIG_SWR_INTR_CLEAR_2 (ROULEUR_DIG_BASE_ADDR+0x089) +#define ROULEUR_DIG_SWR_INTR_LEVEL_0 (ROULEUR_DIG_BASE_ADDR+0x08A) +#define ROULEUR_DIG_SWR_INTR_LEVEL_1 (ROULEUR_DIG_BASE_ADDR+0x08B) +#define ROULEUR_DIG_SWR_INTR_LEVEL_2 (ROULEUR_DIG_BASE_ADDR+0x08C) +#define ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL (ROULEUR_DIG_BASE_ADDR+0x093) +#define ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL (ROULEUR_DIG_BASE_ADDR+0x094) +#define ROULEUR_DIG_SWR_LOOP_BACK_MODE (ROULEUR_DIG_BASE_ADDR+0x097) +#define ROULEUR_DIG_SWR_DRIVE_STRENGTH_0 (ROULEUR_DIG_BASE_ADDR+0x0A0) +#define ROULEUR_DIG_SWR_DIG_DEBUG_CTL (ROULEUR_DIG_BASE_ADDR+0x0AB) +#define ROULEUR_DIG_SWR_DIG_DEBUG_EN (ROULEUR_DIG_BASE_ADDR+0x0AC) +#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA0 (ROULEUR_DIG_BASE_ADDR+0x0B0) +#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA1 (ROULEUR_DIG_BASE_ADDR+0x0B1) +#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA2 (ROULEUR_DIG_BASE_ADDR+0x0B2) +#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA3 (ROULEUR_DIG_BASE_ADDR+0x0B3) + +#define ROULEUR_ANALOG_REGISTERS_MAX_SIZE (ROULEUR_ANA_BASE_ADDR+0x0B5) +#define ROULEUR_DIGITAL_REGISTERS_MAX_SIZE (ROULEUR_DIG_BASE_ADDR+0x0B4) +#define ROULEUR_ANALOG_MAX_REGISTER (ROULEUR_ANALOG_REGISTERS_MAX_SIZE - 1) +#define ROULEUR_DIGITAL_MAX_REGISTER (ROULEUR_DIGITAL_REGISTERS_MAX_SIZE - 1) +#endif diff --git a/asoc/codecs/rouleur/rouleur-regmap.c b/asoc/codecs/rouleur/rouleur-regmap.c new file mode 100644 index 00000000..62502140 --- /dev/null +++ b/asoc/codecs/rouleur/rouleur-regmap.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/regmap.h> +#include <linux/device.h> +#include "rouleur-registers.h" + +extern const u8 rouleur_reg_access_analog[ + ROULEUR_REG(ROULEUR_ANALOG_REGISTERS_MAX_SIZE)]; +extern const u8 rouleur_reg_access_digital[ + ROULEUR_REG(ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)]; + +static const struct reg_default rouleur_defaults[] = { + { ROULEUR_ANA_MICBIAS_MICB_1_2_EN, 0x01 }, + { ROULEUR_ANA_MICBIAS_MICB_3_EN, 0x00 }, + { ROULEUR_ANA_MICBIAS_LDO_1_SETTING, 0x21 }, + { ROULEUR_ANA_MICBIAS_LDO_1_CTRL, 0x01 }, + { ROULEUR_ANA_TX_AMIC1, 0x00 }, + { ROULEUR_ANA_TX_AMIC2, 0x00 }, + { ROULEUR_ANA_MBHC_MECH, 0x39 }, + { ROULEUR_ANA_MBHC_ELECT, 0x08 }, + { ROULEUR_ANA_MBHC_ZDET, 0x10 }, + { ROULEUR_ANA_MBHC_RESULT_1, 0x00 }, + { ROULEUR_ANA_MBHC_RESULT_2, 0x00 }, + { ROULEUR_ANA_MBHC_RESULT_3, 0x00 }, + { ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1, 0x00 }, + { ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2, 0x10 }, + { ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3, 0x20 }, + { ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400, 0x30 }, + { ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400, 0x40 }, + { ROULEUR_ANA_MBHC_MICB2_RAMP, 0x00 }, + { ROULEUR_ANA_MBHC_CTL_1, 0x02 }, + { ROULEUR_ANA_MBHC_CTL_2, 0x05 }, + { ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0xE9 }, + { ROULEUR_ANA_MBHC_ZDET_ANA_CTL, 0x0F }, + { ROULEUR_ANA_MBHC_ZDET_RAMP_CTL, 0x00 }, + { ROULEUR_ANA_MBHC_FSM_STATUS, 0x00 }, + { ROULEUR_ANA_MBHC_ADC_RESULT, 0x00 }, + { ROULEUR_ANA_MBHC_MCLK, 0x30 }, + { ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT, 0x00 }, + { ROULEUR_ANA_NCP_EN, 0x00 }, + { ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x2B }, + { ROULEUR_ANA_HPHPA_PA_STATUS, 0x00 }, + { ROULEUR_ANA_HPHPA_FSM_CLK, 0x12 }, + { ROULEUR_ANA_HPHPA_SPARE_CTL, 0x02 }, + { ROULEUR_ANA_SURGE_EN, 0x38 }, + { ROULEUR_ANA_COMBOPA_CTL, 0x35 }, + { ROULEUR_ANA_RXLDO_CTL, 0x86 }, + { ROULEUR_ANA_MBIAS_EN, 0x00 }, + { ROULEUR_DIG_SWR_CHIP_ID0, 0x00 }, + { ROULEUR_DIG_SWR_CHIP_ID1, 0x00 }, + { ROULEUR_DIG_SWR_CHIP_ID2, 0x0C }, + { ROULEUR_DIG_SWR_CHIP_ID3, 0x01 }, + { ROULEUR_DIG_SWR_SWR_TX_CLK_RATE, 0x00 }, + { ROULEUR_DIG_SWR_CDC_RST_CTL, 0x03 }, + { ROULEUR_DIG_SWR_TOP_CLK_CFG, 0x00 }, + { ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x00 }, + { ROULEUR_DIG_SWR_CDC_TX_CLK_CTL, 0x33 }, + { ROULEUR_DIG_SWR_SWR_RST_EN, 0x00 }, + { ROULEUR_DIG_SWR_CDC_RX_RST, 0x00 }, + { ROULEUR_DIG_SWR_CDC_RX0_CTL, 0xFC }, + { ROULEUR_DIG_SWR_CDC_RX1_CTL, 0xFC }, + { ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1, 0x00 }, + { ROULEUR_DIG_SWR_CDC_COMP_CTL_0, 0x00 }, + { ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL, 0x66 }, + { ROULEUR_DIG_SWR_CDC_RX_GAIN_0, 0x55 }, + { ROULEUR_DIG_SWR_CDC_RX_GAIN_1, 0xA9 }, + { ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL, 0x00 }, + { ROULEUR_DIG_SWR_CDC_TX0_CTL, 0x68 }, + { ROULEUR_DIG_SWR_CDC_TX1_CTL, 0x68 }, + { ROULEUR_DIG_SWR_CDC_TX_RST, 0x00 }, + { ROULEUR_DIG_SWR_CDC_REQ0_CTL, 0x01 }, + { ROULEUR_DIG_SWR_CDC_REQ1_CTL, 0x01 }, + { ROULEUR_DIG_SWR_CDC_RST, 0x00 }, + { ROULEUR_DIG_SWR_CDC_AMIC_CTL, 0x02 }, + { ROULEUR_DIG_SWR_CDC_DMIC_CTL, 0x00 }, + { ROULEUR_DIG_SWR_CDC_DMIC1_CTL, 0x00 }, + { ROULEUR_DIG_SWR_CDC_DMIC1_RATE, 0x01 }, + { ROULEUR_DIG_SWR_PDM_WD_CTL0, 0x00 }, + { ROULEUR_DIG_SWR_PDM_WD_CTL1, 0x00 }, + { ROULEUR_DIG_SWR_INTR_MODE, 0x00 }, + { ROULEUR_DIG_SWR_INTR_MASK_0, 0xFF }, + { ROULEUR_DIG_SWR_INTR_MASK_1, 0x7F }, + { ROULEUR_DIG_SWR_INTR_MASK_2, 0x0C }, + { ROULEUR_DIG_SWR_INTR_STATUS_0, 0x00 }, + { ROULEUR_DIG_SWR_INTR_STATUS_1, 0x00 }, + { ROULEUR_DIG_SWR_INTR_STATUS_2, 0x00 }, + { ROULEUR_DIG_SWR_INTR_CLEAR_0, 0x00 }, + { ROULEUR_DIG_SWR_INTR_CLEAR_1, 0x00 }, + { ROULEUR_DIG_SWR_INTR_CLEAR_2, 0x00 }, + { ROULEUR_DIG_SWR_INTR_LEVEL_0, 0x00 }, + { ROULEUR_DIG_SWR_INTR_LEVEL_1, 0x2A }, + { ROULEUR_DIG_SWR_INTR_LEVEL_2, 0x00 }, + { ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL, 0x00 }, + { ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL, 0x00 }, + { ROULEUR_DIG_SWR_LOOP_BACK_MODE, 0x00 }, + { ROULEUR_DIG_SWR_DRIVE_STRENGTH_0, 0x00 }, + { ROULEUR_DIG_SWR_DIG_DEBUG_CTL, 0x00 }, + { ROULEUR_DIG_SWR_DIG_DEBUG_EN, 0x00 }, + { ROULEUR_DIG_SWR_DEM_BYPASS_DATA0, 0x55 }, + { ROULEUR_DIG_SWR_DEM_BYPASS_DATA1, 0x55 }, + { ROULEUR_DIG_SWR_DEM_BYPASS_DATA2, 0x55 }, + { ROULEUR_DIG_SWR_DEM_BYPASS_DATA3, 0x01 }, +}; + +static bool rouleur_readable_register(struct device *dev, unsigned int reg) +{ + if (reg > ROULEUR_ANA_BASE_ADDR && reg < + ROULEUR_ANALOG_REGISTERS_MAX_SIZE) + return rouleur_reg_access_analog[ROULEUR_REG(reg)] & RD_REG; + if (reg > ROULEUR_DIG_BASE_ADDR && reg < + ROULEUR_DIGITAL_REGISTERS_MAX_SIZE) + return rouleur_reg_access_digital[ROULEUR_REG(reg)] & RD_REG; + return 0; +} + +static bool rouleur_writeable_register(struct device *dev, unsigned int reg) +{ + if (reg > ROULEUR_ANA_BASE_ADDR && reg < + ROULEUR_ANALOG_REGISTERS_MAX_SIZE) + return rouleur_reg_access_analog[ROULEUR_REG(reg)] & WR_REG; + if (reg > ROULEUR_DIG_BASE_ADDR && reg < + ROULEUR_DIGITAL_REGISTERS_MAX_SIZE) + return rouleur_reg_access_digital[ROULEUR_REG(reg)] & WR_REG; + return 0; +} + +static bool rouleur_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg > ROULEUR_ANA_BASE_ADDR && reg < + ROULEUR_ANALOG_REGISTERS_MAX_SIZE) + if ((rouleur_reg_access_analog[ROULEUR_REG(reg)] & RD_REG) + && !(rouleur_reg_access_analog[ROULEUR_REG(reg)] & WR_REG)) + return true; + return false; + if (reg > ROULEUR_DIG_BASE_ADDR && reg < + ROULEUR_DIGITAL_REGISTERS_MAX_SIZE) + if ((rouleur_reg_access_digital[ROULEUR_REG(reg)] & RD_REG) + && !(rouleur_reg_access_digital[ROULEUR_REG(reg)] & WR_REG)) + return true; + return false; + return 0; +} + +struct regmap_config rouleur_regmap_config = { + .name = "rouleur_csr", + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rouleur_defaults, + .num_reg_defaults = ARRAY_SIZE(rouleur_defaults), + .max_register = ROULEUR_ANALOG_MAX_REGISTER + + ROULEUR_DIGITAL_MAX_REGISTER, + .readable_reg = rouleur_readable_register, + .writeable_reg = rouleur_writeable_register, + .volatile_reg = rouleur_volatile_register, + .can_multi_write = true, +}; diff --git a/asoc/codecs/rouleur/rouleur-tables.c b/asoc/codecs/rouleur/rouleur-tables.c new file mode 100644 index 00000000..ea710ebb --- /dev/null +++ b/asoc/codecs/rouleur/rouleur-tables.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/types.h> +#include "rouleur-registers.h" + +const u8 rouleur_reg_access_analog[ROULEUR_REG( + ROULEUR_ANALOG_REGISTERS_MAX_SIZE)] = { + [ROULEUR_REG(ROULEUR_ANA_MICBIAS_MICB_1_2_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MICBIAS_MICB_3_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MICBIAS_LDO_1_SETTING)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MICBIAS_LDO_1_CTRL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_TX_AMIC1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_TX_AMIC2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_MECH)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ELECT)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_1)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_2)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_3)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_MICB2_RAMP)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_CTL_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_CTL_2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_PLUG_DETECT_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_ANA_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_RAMP_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_FSM_STATUS)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ADC_RESULT)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_MCLK)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_NCP_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_HPHPA_CNP_CTL_2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_HPHPA_PA_STATUS)] = RD_REG, + [ROULEUR_REG(ROULEUR_ANA_HPHPA_FSM_CLK)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_HPHPA_SPARE_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_SURGE_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_COMBOPA_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_RXLDO_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_ANA_MBIAS_EN)] = RD_WR_REG, +}; + +const u8 rouleur_reg_access_digital[ROULEUR_REG( + ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)] = { + [ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID0)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID1)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID2)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID3)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_SWR_TX_CLK_RATE)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RST_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_TOP_CLK_CFG)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_CLK_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_CLK_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_SWR_RST_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_RST)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX0_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX1_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_COMP_CTL_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX0_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX1_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_RST)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_REQ0_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_REQ1_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RST)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_AMIC_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC1_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC1_RATE)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_PDM_WD_CTL0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_PDM_WD_CTL1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MODE)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_0)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_1)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_2)] = RD_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_LOOP_BACK_MODE)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DRIVE_STRENGTH_0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DIG_DEBUG_CTL)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DIG_DEBUG_EN)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA0)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA1)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA2)] = RD_WR_REG, + [ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA3)] = RD_WR_REG, +}; diff --git a/asoc/codecs/rouleur/rouleur.c b/asoc/codecs/rouleur/rouleur.c new file mode 100644 index 00000000..14dfd52f --- /dev/null +++ b/asoc/codecs/rouleur/rouleur.c @@ -0,0 +1,2492 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/component.h> +#include <linux/regmap.h> +#include <linux/pm_runtime.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <soc/soundwire.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include "internal.h" +#include "rouleur.h" +#include <asoc/wcdcal-hwdep.h> +#include "rouleur-registers.h" +#include "pm2250-spmi.h" +#include <asoc/msm-cdc-pinctrl.h> +#include <dt-bindings/sound/audio-codec-port-types.h> +#include <asoc/msm-cdc-supply.h> + +#define DRV_NAME "rouleur_codec" + +#define NUM_SWRS_DT_PARAMS 5 + +#define ROULEUR_VERSION_1_0 1 +#define ROULEUR_VERSION_ENTRY_SIZE 32 + +#define NUM_ATTEMPTS 5 + +enum { + CODEC_TX = 0, + CODEC_RX, +}; + +enum { + ALLOW_VPOS_DISABLE, + HPH_COMP_DELAY, + HPH_PA_DELAY, + AMIC2_BCS_ENABLE, +}; + +/* TODO: Check on the step values */ +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static int rouleur_handle_post_irq(void *data); +static int rouleur_reset(struct device *dev, int val); + +static const struct regmap_irq ROULEUR_IRQs[ROULEUR_NUM_IRQS] = { + REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(ROULEUR_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(ROULEUR_IRQ_EAR_OCP_INT, 1, 0x04), + REGMAP_IRQ_REG(ROULEUR_IRQ_LO_CNP_INT, 1, 0x08), + REGMAP_IRQ_REG(ROULEUR_IRQ_LO_OCP_INT, 1, 0x10), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip rouleur_regmap_irq_chip = { + .name = "rouleur", + .irqs = ROULEUR_IRQs, + .num_irqs = ARRAY_SIZE(ROULEUR_IRQs), + .num_regs = 3, + .status_base = ROULEUR_DIG_SWR_INTR_STATUS_0, + .mask_base = ROULEUR_DIG_SWR_INTR_MASK_0, + .ack_base = ROULEUR_DIG_SWR_INTR_CLEAR_0, + .use_ack = 1, + .type_base = ROULEUR_DIG_SWR_INTR_LEVEL_0, + .runtime_pm = false, + .handle_post_irq = rouleur_handle_post_irq, + .irq_drv_data = NULL, +}; + +static int rouleur_handle_post_irq(void *data) +{ + struct rouleur_priv *rouleur = data; + u32 status1 = 0, status2 = 0, status3 = 0; + + regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_0, &status1); + regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_1, &status2); + regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_2, &status3); + + rouleur->tx_swr_dev->slave_irq_pending = + ((status1 || status2 || status3) ? true : false); + + return IRQ_HANDLED; +} + +static int rouleur_init_reg(struct snd_soc_component *component) +{ + /* Enable surge protection */ + snd_soc_component_update_bits(component, ROULEUR_ANA_SURGE_EN, + 0xC0, 0xC0); + return 0; +} + +static int rouleur_set_port_params(struct snd_soc_component *component, + 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 rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + switch (path) { + case CODEC_RX: + map = &rouleur->rx_port_mapping; + num_ports = rouleur->num_rx_ports; + break; + case CODEC_TX: + map = &rouleur->tx_port_mapping; + num_ports = rouleur->num_tx_ports; + break; + default: + dev_err(component->dev, "%s Invalid path: %d\n", + __func__, path); + return -EINVAL; + } + + 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; + } + } + + dev_err(component->dev, "%s Failed to find slave port for type %u\n", + __func__, slv_prt_type); + return -EINVAL; +found: + *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 rouleur_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 rouleur_priv *rouleur = dev_get_drvdata(dev); + + switch (path) { + case CODEC_RX: + map = &rouleur->rx_port_mapping; + num_ports = &rouleur->num_rx_ports; + break; + case CODEC_TX: + map = &rouleur->tx_port_mapping; + num_ports = &rouleur->num_tx_ports; + break; + default: + dev_err(dev, "%s Invalid path: %d\n", + __func__, path); + return -EINVAL; + } + + 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; + +err_pdata_fail: + kfree(dt_array); +err: + return ret; +} + +static int rouleur_tx_connect_port(struct snd_soc_component *component, + u8 slv_port_type, u8 enable) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + u8 port_id; + u8 num_ch; + u8 ch_mask; + u32 ch_rate; + u8 port_type; + u8 num_port = 1; + int ret = 0; + + ret = rouleur_set_port_params(component, slv_port_type, &port_id, + &num_ch, &ch_mask, &ch_rate, + &port_type, CODEC_TX); + + if (ret) { + dev_err(rouleur->dev, "%s:Failed to set port params: %d\n", + __func__, ret); + return ret; + } + + if (enable) + ret = swr_connect_port(rouleur->tx_swr_dev, &port_id, + num_port, &ch_mask, &ch_rate, + &num_ch, &port_type); + else + ret = swr_disconnect_port(rouleur->tx_swr_dev, &port_id, + num_port, &ch_mask, &port_type); + return ret; + +} +static int rouleur_rx_connect_port(struct snd_soc_component *component, + u8 slv_port_type, u8 enable) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + u8 port_id; + u8 num_ch; + u8 ch_mask; + u32 ch_rate; + u8 port_type; + u8 num_port = 1; + int ret = 0; + + ret = rouleur_set_port_params(component, slv_port_type, &port_id, + &num_ch, &ch_mask, &ch_rate, + &port_type, CODEC_RX); + + if (ret) { + dev_err(rouleur->dev, "%s:Failed to set port params: %d\n", + __func__, ret); + return ret; + } + + if (enable) + ret = swr_connect_port(rouleur->rx_swr_dev, &port_id, + num_port, &ch_mask, &ch_rate, + &num_ch, &port_type); + else + ret = swr_disconnect_port(rouleur->rx_swr_dev, &port_id, + num_port, &ch_mask, &port_type); + return ret; +} + +static int rouleur_global_mbias_enable(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + mutex_lock(&rouleur->main_bias_lock); + if (rouleur->mbias_cnt == 0) { + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBIAS_EN, 0x20, 0x20); + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBIAS_EN, 0x10, 0x10); + usleep_range(1000, 1100); + } + rouleur->mbias_cnt++; + mutex_unlock(&rouleur->main_bias_lock); + + return 0; +} + +static int rouleur_global_mbias_disable(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + mutex_lock(&rouleur->main_bias_lock); + if (rouleur->mbias_cnt == 0) { + dev_dbg(rouleur->dev, "%s:mbias already disabled\n", __func__); + mutex_unlock(&rouleur->main_bias_lock); + return 0; + } + rouleur->mbias_cnt--; + if (rouleur->mbias_cnt == 0) { + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBIAS_EN, 0x10, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_MBIAS_EN, 0x20, 0x00); + } + mutex_unlock(&rouleur->main_bias_lock); + + return 0; +} + +static int rouleur_rx_clk_enable(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + mutex_lock(&rouleur->rx_clk_lock); + if (rouleur->rx_clk_cnt == 0) { + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x10, 0x10); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x20, 0x20); + usleep_range(5000, 5100); + rouleur_global_mbias_enable(component); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_FSM_CLK, 0x11, 0x11); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_FSM_CLK, 0x80, 0x80); + snd_soc_component_update_bits(component, + ROULEUR_ANA_NCP_EN, 0x01, 0x01); + usleep_range(500, 510); + } + rouleur->rx_clk_cnt++; + mutex_unlock(&rouleur->rx_clk_lock); + + return 0; +} + +static int rouleur_rx_clk_disable(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + mutex_lock(&rouleur->rx_clk_lock); + if (rouleur->rx_clk_cnt == 0) { + dev_dbg(rouleur->dev, "%s:clk already disabled\n", __func__); + mutex_unlock(&rouleur->rx_clk_lock); + return 0; + } + rouleur->rx_clk_cnt--; + if (rouleur->rx_clk_cnt == 0) { + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_FSM_CLK, 0x80, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_FSM_CLK, 0x11, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_NCP_EN, 0x01, 0x00); + rouleur_global_mbias_disable(component); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x20, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x10, 0x00); + + } + mutex_unlock(&rouleur->rx_clk_lock); + return 0; +} + +/* + * rouleur_soc_get_mbhc: get rouleur_mbhc handle of corresponding component + * @component: handle to snd_soc_component * + * + * return rouleur_mbhc handle or error code in case of failure + */ +struct rouleur_mbhc *rouleur_soc_get_mbhc(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur; + + if (!component) { + pr_err("%s: Invalid params, NULL component\n", __func__); + return NULL; + } + rouleur = snd_soc_component_get_drvdata(component); + + if (!rouleur) { + pr_err("%s: Invalid params, NULL tavil\n", __func__); + return NULL; + } + + return rouleur->mbhc; +} +EXPORT_SYMBOL(rouleur_soc_get_mbhc); + +static int rouleur_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_rx_clk_enable(component); + set_bit(HPH_COMP_DELAY, &rouleur->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (rouleur->comp1_enable) { + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x02, 0x02); + + if (rouleur->comp2_enable) + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x01, 0x01); + /* + * 5ms sleep is required after COMP is enabled as per + * HW requirement + */ + if (test_bit(HPH_COMP_DELAY, &rouleur->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_COMP_DELAY, + &rouleur->status_mask); + } + } else { + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x02, 0x00); + } + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX0_CTL, + 0x7C, 0x7C); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL, + 0x04, 0x04); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x01, 0x01); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL0, + 0x03, 0x03); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL0, + 0x03, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, + 0x01, 0x00); + break; + } + + return 0; +} + +static int rouleur_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_rx_clk_enable(component); + set_bit(HPH_COMP_DELAY, &rouleur->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (rouleur->comp2_enable) { + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x01, 0x01); + + if (rouleur->comp1_enable) + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x02, 0x02); + /* + * 5ms sleep is required after COMP is enabled as per + * HW requirement + */ + if (test_bit(HPH_COMP_DELAY, &rouleur->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_COMP_DELAY, + &rouleur->status_mask); + } + } else { + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_COMP_CTL_0, + 0x01, 0x00); + } + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX1_CTL, + 0x7C, 0x7C); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL, + 0x08, 0x08); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x02, 0x02); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL1, + 0x03, 0x03); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL1, + 0x03, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x02, 0x00); + break; + + } + + return 0; +} + +static int rouleur_codec_ear_lo_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_rx_clk_enable(component); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX0_CTL, + 0x7C, 0x7C); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, + 0x01, 0x01); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL0, + 0x03, 0x03); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL, + 0x04, 0x04); + + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_PDM_WD_CTL0, + 0x03, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, + 0x01, 0x00); + + break; + }; + return 0; + +} + +static int rouleur_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev, + rouleur->rx_swr_dev->dev_num, + true); + + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x40, 0x40); + set_bit(HPH_PA_DELAY, &rouleur->status_mask); + /* TODO: WHY SECOND TIME */ + ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev, + rouleur->rx_swr_dev->dev_num, + true); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement. + */ + if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_PA_DELAY, &rouleur->status_mask); + } + + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10)); + wcd_enable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHR_PDM_WD_INT); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_disable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHR_PDM_WD_INT); + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX2 << 0x10 | 0x1)); + blocking_notifier_call_chain(&rouleur->mbhc->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &rouleur->mbhc->wcd_mbhc); + set_bit(HPH_PA_DELAY, &rouleur->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, &rouleur->status_mask)) { + + usleep_range(5000, 5100); + clear_bit(HPH_PA_DELAY, &rouleur->status_mask); + } + + blocking_notifier_call_chain(&rouleur->mbhc->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &rouleur->mbhc->wcd_mbhc); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x40, 0x00); + break; + }; + return ret; +} + +static int rouleur_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev, + rouleur->rx_swr_dev->dev_num, + true); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x80, 0x80); + set_bit(HPH_PA_DELAY, &rouleur->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement. + */ + if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_PA_DELAY, &rouleur->status_mask); + } + + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + wcd_enable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_disable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + blocking_notifier_call_chain(&rouleur->mbhc->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &rouleur->mbhc->wcd_mbhc); + set_bit(HPH_PA_DELAY, &rouleur->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement. + */ + if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) { + usleep_range(5000, 5100); + clear_bit(HPH_PA_DELAY, &rouleur->status_mask); + } + + blocking_notifier_call_chain(&rouleur->mbhc->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &rouleur->mbhc->wcd_mbhc); + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0x80, 0x00); + + break; + }; + return ret; +} + +static int rouleur_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev, + rouleur->rx_swr_dev->dev_num, + true); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x80); + usleep_range(5000, 5100); + break; + case SND_SOC_DAPM_POST_PMU: + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + wcd_enable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_disable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x00); + usleep_range(5000, 5100); + }; + return ret; +} + +static int rouleur_codec_enable_lo_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev, + rouleur->rx_swr_dev->dev_num, + true); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x40, 0x40); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x80); + usleep_range(5000, 5100); + break; + case SND_SOC_DAPM_POST_PMU: + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10)); + wcd_enable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_disable_irq(&rouleur->irq_info, + ROULEUR_IRQ_HPHL_PDM_WD_INT); + if (rouleur->update_wcd_event) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_RX_MUTE, + (WCD_RX1 << 0x10 | 0x1)); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x40, 0x00); + usleep_range(5000, 5100); + }; + return ret; +} + +static int rouleur_enable_rx1(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_rx_connect_port(component, HPH_L, true); + if (rouleur->comp1_enable) + rouleur_rx_connect_port(component, COMP_L, true); + break; + case SND_SOC_DAPM_POST_PMD: + rouleur_rx_connect_port(component, HPH_L, false); + if (rouleur->comp1_enable) + rouleur_rx_connect_port(component, COMP_L, false); + rouleur_rx_clk_disable(component); + break; + }; + return 0; +} + +static int rouleur_enable_rx2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_rx_connect_port(component, HPH_R, true); + if (rouleur->comp2_enable) + rouleur_rx_connect_port(component, COMP_R, true); + break; + case SND_SOC_DAPM_POST_PMD: + rouleur_rx_connect_port(component, HPH_R, false); + if (rouleur->comp2_enable) + rouleur_rx_connect_port(component, COMP_R, false); + rouleur_rx_clk_disable(component); + break; + }; + + return 0; +} + +static int rouleur_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + char *wname; + int ret = 0; + + wname = strpbrk(w->name, "01"); + + if (!wname) { + dev_err(component->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(component->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(rouleur->dmic_0_1_clk_cnt); + dmic_clk_reg = ROULEUR_DIG_SWR_CDC_DMIC1_CTL; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(component->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_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_AMIC_CTL, 0x02, 0x00); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x08, 0x08); + rouleur_tx_connect_port(component, DMIC0 + (w->shift), true); + break; + case SND_SOC_DAPM_POST_PMD: + rouleur_tx_connect_port(component, DMIC0 + (w->shift), false); + snd_soc_component_update_bits(component, + dmic_clk_reg, 0x08, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_AMIC_CTL, 0x02, 0x02); + break; + + }; + return 0; +} + +static int rouleur_tx_swr_ctrl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = swr_slvdev_datapath_control(rouleur->tx_swr_dev, + rouleur->tx_swr_dev->dev_num, + true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = swr_slvdev_datapath_control(rouleur->tx_swr_dev, + rouleur->tx_swr_dev->dev_num, + false); + break; + }; + + return ret; +} + +static int rouleur_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = + snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable BCS for Headset mic */ + if (w->shift == 1 && !(snd_soc_component_read32(component, + ROULEUR_ANA_TX_AMIC2) & 0x10)) { + rouleur_tx_connect_port(component, MBHC, true); + set_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask); + } + rouleur_tx_connect_port(component, ADC1 + (w->shift), true); + rouleur_global_mbias_enable(component); + if (w->shift) + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1, + 0x30, 0x30); + else + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1, + 0x03, 0x03); + break; + case SND_SOC_DAPM_POST_PMD: + rouleur_tx_connect_port(component, ADC1 + (w->shift), false); + if (w->shift == 1 && + test_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask)) { + rouleur_tx_connect_port(component, MBHC, false); + clear_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask); + } + if (w->shift) + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1, + 0x30, 0x00); + else + snd_soc_component_update_bits(component, + ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1, + 0x03, 0x00); + rouleur_global_mbias_disable(component); + break; + }; + + return 0; +} + +/* + * rouleur_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int rouleur_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1.6V and maximum is 2.85V */ + if (micb_mv < 1600 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1600) / 50; +} +EXPORT_SYMBOL(rouleur_get_micb_vout_ctl_val); + +/* + * rouleur_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @component: handle to snd_soc_component * + * @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 rouleur_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct rouleur_priv *rouleur = + snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + int pullup_mask; + + micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN; + switch (micb_num) { + case MIC_BIAS_1: + micb_val = snd_soc_component_read32(component, micb_reg); + micb_en = (micb_val & 0x40) >> 6; + pullup_mask = 0x20; + break; + case MIC_BIAS_2: + micb_val = snd_soc_component_read32(component, micb_reg); + micb_en = (micb_val & 0x04) >> 2; + pullup_mask = 0x02; + break; + case MIC_BIAS_3: + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&rouleur->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. + */ + cur_vout_ctl = (snd_soc_component_read32(component, + ROULEUR_ANA_MICBIAS_LDO_1_SETTING)) & 0xF8; + + req_vout_ctl = rouleur_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(component->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_component_update_bits(component, micb_reg, pullup_mask, + pullup_mask); + + snd_soc_component_update_bits(component, + ROULEUR_ANA_MICBIAS_LDO_1_SETTING, 0xF8, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_component_update_bits(component, micb_reg, + pullup_mask, 0x00); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&rouleur->micb_lock); + return ret; +} +EXPORT_SYMBOL(rouleur_mbhc_micb_adjust_voltage); + +int rouleur_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + 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; + u8 pullup_mask = 0, enable_mask = 0; + + if ((micb_index < 0) || (micb_index > ROULEUR_MAX_MICBIAS - 1)) { + dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN; + pullup_mask = 0x20; + enable_mask = 0x40; + break; + case MIC_BIAS_2: + micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN; + pullup_mask = 0x02; + enable_mask = 0x04; + 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 = ROULEUR_ANA_MICBIAS_MICB_3_EN; + pullup_mask = 0x02; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + }; + mutex_lock(&rouleur->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + rouleur->pullup_ref[micb_index]++; + if ((rouleur->pullup_ref[micb_index] == 1) && + (rouleur->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + pullup_mask, pullup_mask); + break; + case MICB_PULLUP_DISABLE: + if (rouleur->pullup_ref[micb_index] > 0) + rouleur->pullup_ref[micb_index]--; + if ((rouleur->pullup_ref[micb_index] == 0) && + (rouleur->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + pullup_mask, 0x00); + break; + case MICB_ENABLE: + rouleur->micb_ref[micb_index]++; + if (rouleur->micb_ref[micb_index] == 1) { + rouleur_global_mbias_enable(component); + snd_soc_component_update_bits(component, + micb_reg, enable_mask, enable_mask); + if (post_on_event) + blocking_notifier_call_chain( + &rouleur->mbhc->notifier, post_on_event, + &rouleur->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && rouleur->mbhc) + blocking_notifier_call_chain( + &rouleur->mbhc->notifier, post_dapm_on, + &rouleur->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + if (rouleur->micb_ref[micb_index] > 0) + rouleur->micb_ref[micb_index]--; + if ((rouleur->micb_ref[micb_index] == 0) && + (rouleur->pullup_ref[micb_index] == 0)) { + if (pre_off_event && rouleur->mbhc) + blocking_notifier_call_chain( + &rouleur->mbhc->notifier, pre_off_event, + &rouleur->mbhc->wcd_mbhc); + snd_soc_component_update_bits(component, micb_reg, + enable_mask, 0x00); + rouleur_global_mbias_disable(component); + if (post_off_event && rouleur->mbhc) + blocking_notifier_call_chain( + &rouleur->mbhc->notifier, + post_off_event, + &rouleur->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_off && rouleur->mbhc) + blocking_notifier_call_chain( + &rouleur->mbhc->notifier, post_dapm_off, + &rouleur->mbhc->wcd_mbhc); + break; + }; + + dev_dbg(component->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, rouleur->micb_ref[micb_index], + rouleur->pullup_ref[micb_index]); + mutex_unlock(&rouleur->micb_lock); + + return 0; +} +EXPORT_SYMBOL(rouleur_micbias_control); + +void rouleur_disable_bcs_before_slow_insert(struct snd_soc_component *component, + bool bcs_disable) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + if (rouleur->update_wcd_event) { + if (bcs_disable) + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_BCS_CLK_OFF, 0); + else + rouleur->update_wcd_event(rouleur->handle, + WCD_BOLERO_EVT_BCS_CLK_OFF, 1); + } +} + +static int rouleur_get_logical_addr(struct swr_device *swr_dev) +{ + int ret = 0; + uint8_t devnum = 0; + int num_retry = NUM_ATTEMPTS; + + do { + 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); + /* retry after 1ms */ + usleep_range(1000, 1010); + } + } while (ret && --num_retry); + swr_dev->dev_num = devnum; + return 0; +} + +static int rouleur_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + u16 event = (val & 0xffff); + int ret = 0; + struct rouleur_priv *rouleur = dev_get_drvdata((struct device *)data); + struct snd_soc_component *component = rouleur->component; + struct wcd_mbhc *mbhc; + + switch (event) { + case BOLERO_WCD_EVT_PA_OFF_PRE_SSR: + snd_soc_component_update_bits(component, + ROULEUR_ANA_HPHPA_CNP_CTL_2, + 0xC0, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x40, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x00); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x40, 0x40); + snd_soc_component_update_bits(component, + ROULEUR_ANA_COMBOPA_CTL, + 0x80, 0x00); + break; + case BOLERO_WCD_EVT_SSR_DOWN: + rouleur->mbhc->wcd_mbhc.deinit_in_progress = true; + mbhc = &rouleur->mbhc->wcd_mbhc; + rouleur_mbhc_ssr_down(rouleur->mbhc, component); + rouleur_reset(rouleur->dev, 0x01); + break; + case BOLERO_WCD_EVT_SSR_UP: + rouleur_reset(rouleur->dev, 0x00); + /* allow reset to take effect */ + usleep_range(10000, 10010); + rouleur_get_logical_addr(rouleur->tx_swr_dev); + rouleur_get_logical_addr(rouleur->rx_swr_dev); + + rouleur_init_reg(component); + regcache_mark_dirty(rouleur->regmap); + regcache_sync(rouleur->regmap); + /* Initialize MBHC module */ + mbhc = &rouleur->mbhc->wcd_mbhc; + ret = rouleur_mbhc_post_ssr_init(rouleur->mbhc, component); + if (ret) { + dev_err(component->dev, "%s: mbhc initialization failed\n", + __func__); + } else { + rouleur_mbhc_hs_detect(component, mbhc->mbhc_cfg); + } + rouleur->mbhc->wcd_mbhc.deinit_in_progress = false; + break; + default: + dev_err(component->dev, "%s: invalid event %d\n", __func__, + event); + break; + } + return 0; +} + +static int __rouleur_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int micb_num; + + dev_dbg(component->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: + /* Micbias LD0 enable not supported for MicBias 3*/ + if (micb_num == MIC_BIAS_3) + rouleur_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + else + rouleur_micbias_control(component, micb_num, + MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + if (micb_num == MIC_BIAS_3) + rouleur_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + else + rouleur_micbias_control(component, micb_num, + MICB_DISABLE, true); + break; + }; + + return 0; + +} + +static int rouleur_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __rouleur_codec_enable_micbias(w, event); +} + +static int __rouleur_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int micb_num; + + dev_dbg(component->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "VA MIC BIAS1", sizeof("VA MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "VA MIC BIAS2", sizeof("VA MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "VA MIC BIAS3", sizeof("VA MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rouleur_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + rouleur_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + break; + }; + + return 0; + +} + +static int rouleur_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + return __rouleur_codec_enable_micbias_pullup(w, event); +} + +static int rouleur_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + 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 ? rouleur->comp2_enable : + rouleur->comp1_enable; + return 0; +} + +static int rouleur_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + 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) + rouleur->comp2_enable = value; + else + rouleur->comp1_enable = value; + + return 0; +} + +static int rouleur_codec_enable_pa_vpos(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + struct rouleur_pdata *pdata = NULL; + int ret = 0; + + pdata = dev_get_platdata(rouleur->dev); + + if (!pdata) { + dev_err(component->dev, "%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__, + w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (test_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask)) { + dev_dbg(component->dev, + "%s: vpos already in enabled state\n", + __func__); + clear_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask); + return 0; + } + ret = msm_cdc_enable_ondemand_supply(rouleur->dev, + rouleur->supplies, + pdata->regulator, + pdata->num_supplies, + "cdc-pa-vpos"); + if (ret == -EINVAL) { + dev_err(component->dev, "%s: pa vpos is not enabled\n", + __func__); + return ret; + } + clear_bit(ALLOW_VPOS_DISABLE, &rouleur->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_VPOS_DISABLE, &rouleur->status_mask); + break; + } + return 0; +} + +static const struct snd_kcontrol_new rouleur_snd_controls[] = { + SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0, + rouleur_get_compander, rouleur_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0, + rouleur_get_compander, rouleur_set_compander), + + SOC_SINGLE_TLV("HPHL Volume", ROULEUR_ANA_HPHPA_L_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", ROULEUR_ANA_HPHPA_R_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("ADC1 Volume", ROULEUR_ANA_TX_AMIC1, 0, 8, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", ROULEUR_ANA_TX_AMIC2, 0, 8, 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 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 ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new lo_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 struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(ROULEUR_ANA_TX_AMIC2, 4, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + + +static const struct snd_soc_dapm_widget rouleur_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"), + + /*tx widgets*/ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + rouleur_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, + rouleur_codec_enable_adc, + 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), + rouleur_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), + rouleur_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, + rouleur_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, + rouleur_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, + rouleur_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, + rouleur_codec_enable_pa_vpos, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /*rx widgets*/ + SND_SOC_DAPM_PGA_E("EAR PGA", ROULEUR_ANA_COMBOPA_CTL, 7, 0, NULL, 0, + rouleur_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("LO PGA", ROULEUR_ANA_COMBOPA_CTL, 7, 0, NULL, 0, + rouleur_codec_enable_lo_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", ROULEUR_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, + 0, rouleur_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", ROULEUR_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, + 0, rouleur_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, + rouleur_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, + rouleur_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, + rouleur_codec_ear_lo_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_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, + rouleur_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, + rouleur_enable_rx2, 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("LO_RDAC", SND_SOC_NOPM, 0, 0, + lo_rdac_switch, ARRAY_SIZE(lo_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("LO"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + + /* micbias pull up widgets*/ + SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS1", SND_SOC_NOPM, 0, 0, + rouleur_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS2", SND_SOC_NOPM, 0, 0, + rouleur_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS3", SND_SOC_NOPM, 0, 0, + rouleur_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + rouleur_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, + rouleur_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), + rouleur_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), + rouleur_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"), +}; + +static const struct snd_soc_dapm_route rouleur_audio_map[] = { + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1"}, + {"ADC1", NULL, "AMIC1"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP3", "AMIC3"}, + {"ADC2 MUX", "INP2", "AMIC2"}, + + {"IN1_HPHL", NULL, "PA_VPOS"}, + {"RX1", NULL, "IN1_HPHL"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "PA_VPOS"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"RDAC3", NULL, "RX1"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, + + {"RDAC3", NULL, "RX1"}, + {"LO_RDAC", "Switch", "RDAC3"}, + {"LO PGA", NULL, "LO_RDAC"}, + {"LO", NULL, "LO PGA"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, +}; + +static ssize_t rouleur_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct rouleur_priv *priv; + char buffer[ROULEUR_VERSION_ENTRY_SIZE]; + int len = 0; + + priv = (struct rouleur_priv *) entry->private_data; + if (!priv) { + pr_err("%s: rouleur priv is null\n", __func__); + return -EINVAL; + } + + switch (priv->version) { + case ROULEUR_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "rouleur_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 rouleur_info_ops = { + .read = rouleur_version_read, +}; + +/* + * rouleur_info_create_codec_entry - creates rouleur module + * @codec_root: The parent directory + * @component: component instance + * + * Creates rouleur module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_component *component) +{ + struct snd_info_entry *version_entry; + struct rouleur_priv *priv; + struct snd_soc_card *card; + + if (!codec_root || !component) + return -EINVAL; + + priv = snd_soc_component_get_drvdata(component); + if (priv->entry) { + dev_dbg(priv->dev, + "%s:rouleur module already created\n", __func__); + return 0; + } + card = component->card; + priv->entry = snd_info_create_subdir(codec_root->module, + "rouleur", codec_root); + if (!priv->entry) { + dev_dbg(component->dev, "%s: failed to create rouleur entry\n", + __func__); + return -ENOMEM; + } + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + priv->entry); + if (!version_entry) { + dev_dbg(component->dev, "%s: failed to create rouleur version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = priv; + version_entry->size = ROULEUR_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &rouleur_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(rouleur_info_create_codec_entry); + +static int rouleur_set_micbias_data(struct rouleur_priv *rouleur, + struct rouleur_pdata *pdata) +{ + int vout_ctl = 0; + int rc = 0; + + if (!pdata) { + dev_err(rouleur->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl = rouleur_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + if (vout_ctl < 0) { + rc = -EINVAL; + goto done; + } + regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MICBIAS_LDO_1_SETTING, + 0xF8, vout_ctl); +done: + return rc; +} + +static int rouleur_soc_codec_probe(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + int ret = -EINVAL; + + dev_info(component->dev, "%s()\n", __func__); + rouleur = snd_soc_component_get_drvdata(component); + + if (!rouleur) + return -EINVAL; + + rouleur->component = component; + snd_soc_component_init_regmap(component, rouleur->regmap); + + rouleur->fw_data = devm_kzalloc(component->dev, + sizeof(*(rouleur->fw_data)), + GFP_KERNEL); + if (!rouleur->fw_data) { + dev_err(component->dev, "Failed to allocate fw_data\n"); + ret = -ENOMEM; + goto done; + } + + set_bit(WCD9XXX_MBHC_CAL, rouleur->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(rouleur->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, component); + + if (ret < 0) { + dev_err(component->dev, "%s hwdep failed %d\n", __func__, ret); + goto done; + } + + ret = rouleur_mbhc_init(&rouleur->mbhc, component, rouleur->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto done; + } + 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, "ADC1_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "ADC2_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "EAR"); + snd_soc_dapm_ignore_suspend(dapm, "LO"); + snd_soc_dapm_ignore_suspend(dapm, "HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "HPHR"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC1_OUTPUT"); + snd_soc_dapm_ignore_suspend(dapm, "DMIC2_OUTPUT"); + snd_soc_dapm_sync(dapm); + + rouleur_init_reg(component); + + rouleur->version = ROULEUR_VERSION_1_0; + /* Register event notifier */ + rouleur->nblock.notifier_call = rouleur_event_notify; + if (rouleur->register_notifier) { + ret = rouleur->register_notifier(rouleur->handle, + &rouleur->nblock, + true); + if (ret) { + dev_err(component->dev, + "%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } +done: + return ret; +} + +static void rouleur_soc_codec_remove(struct snd_soc_component *component) +{ + struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component); + + if (!rouleur) + return; + + if (rouleur->register_notifier) + rouleur->register_notifier(rouleur->handle, + &rouleur->nblock, + false); +} + +static const struct snd_soc_component_driver soc_codec_dev_rouleur = { + .name = DRV_NAME, + .probe = rouleur_soc_codec_probe, + .remove = rouleur_soc_codec_remove, + .controls = rouleur_snd_controls, + .num_controls = ARRAY_SIZE(rouleur_snd_controls), + .dapm_widgets = rouleur_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rouleur_dapm_widgets), + .dapm_routes = rouleur_audio_map, + .num_dapm_routes = ARRAY_SIZE(rouleur_audio_map), +}; + +#ifdef CONFIG_PM_SLEEP +static int rouleur_suspend(struct device *dev) +{ + struct rouleur_priv *rouleur = NULL; + int ret = 0; + struct rouleur_pdata *pdata = NULL; + + if (!dev) + return -ENODEV; + + rouleur = dev_get_drvdata(dev); + if (!rouleur) + return -EINVAL; + + pdata = dev_get_platdata(rouleur->dev); + + if (!pdata) { + dev_err(dev, "%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + if (test_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask)) { + ret = msm_cdc_disable_ondemand_supply(rouleur->dev, + rouleur->supplies, + pdata->regulator, + pdata->num_supplies, + "cdc-pa-vpos"); + if (ret == -EINVAL) { + dev_err(dev, "%s: pa vpos is not disabled\n", + __func__); + return 0; + } + clear_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask); + } + return 0; +} + +static int rouleur_resume(struct device *dev) +{ + return 0; +} +#endif + +static int rouleur_reset(struct device *dev, int reset_val) +{ + struct rouleur_priv *rouleur = NULL; + + if (!dev) + return -ENODEV; + + rouleur = dev_get_drvdata(dev); + if (!rouleur) + return -EINVAL; + + pm2250_spmi_write(rouleur->spmi_dev, rouleur->reset_reg, reset_val); + + return 0; +} + +static int rouleur_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 rouleur_dt_parse_micbias_info(struct device *dev, + struct rouleur_micbias_setting *mb) +{ + u32 prop_val = 0; + int rc = 0; + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = rouleur_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 = rouleur_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 = rouleur_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__); + } +} + +struct rouleur_pdata *rouleur_populate_dt_data(struct device *dev) +{ + struct rouleur_pdata *pdata = NULL; + u32 reg; + int ret = 0; + + pdata = kzalloc(sizeof(struct rouleur_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->spmi_np = of_parse_phandle(dev->of_node, + "qcom,pmic-spmi-node", 0); + if (!pdata->spmi_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,pmic-spmi-node", + dev->of_node->full_name); + kfree(pdata); + return NULL; + } + + ret = of_property_read_u32(dev->of_node, "qcom,wcd-reset-reg", ®); + if (ret) { + dev_err(dev, "%s: Failed to obtain reset reg value %d\n", + __func__, ret); + kfree(pdata); + return NULL; + } + pdata->reset_reg = reg; + + /* 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__); + kfree(pdata); + 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); + rouleur_dt_parse_micbias_info(dev, &pdata->micbias); + + return pdata; +} + +static int rouleur_wakeup(void *handle, bool enable) +{ + struct rouleur_priv *priv; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + priv = (struct rouleur_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 irqreturn_t rouleur_wd_handle_irq(int irq, void *data) +{ + pr_err_ratelimited("%s: Watchdog interrupt for irq =%d triggered\n", + __func__, irq); + return IRQ_HANDLED; +} + +static int rouleur_bind(struct device *dev) +{ + int ret = 0, i = 0; + struct rouleur_priv *rouleur = NULL; + struct rouleur_pdata *pdata = NULL; + struct wcd_ctrl_platform_data *plat_data = NULL; + struct platform_device *pdev = NULL; + + rouleur = kzalloc(sizeof(struct rouleur_priv), GFP_KERNEL); + if (!rouleur) + return -ENOMEM; + + dev_set_drvdata(dev, rouleur); + + pdata = rouleur_populate_dt_data(dev); + if (!pdata) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + kfree(rouleur); + return -EINVAL; + } + rouleur->dev = dev; + rouleur->dev->platform_data = pdata; + pdev = of_find_device_by_node(pdata->spmi_np); + if (!pdev) { + dev_err(dev, "%s: platform device from SPMI node is NULL\n", + __func__); + ret = -EINVAL; + goto err_bind_all; + } + + rouleur->spmi_dev = &pdev->dev; + rouleur->reset_reg = pdata->reset_reg; + ret = msm_cdc_init_supplies(dev, &rouleur->supplies, + pdata->regulator, pdata->num_supplies); + if (!rouleur->supplies) { + dev_err(dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_bind_all; + } + + plat_data = dev_get_platdata(dev->parent); + if (!plat_data) { + dev_err(dev, "%s: platform data from parent is NULL\n", + __func__); + ret = -EINVAL; + goto err_bind_all; + } + rouleur->handle = (void *)plat_data->handle; + if (!rouleur->handle) { + dev_err(dev, "%s: handle is NULL\n", __func__); + ret = -EINVAL; + goto err_bind_all; + } + rouleur->update_wcd_event = plat_data->update_wcd_event; + if (!rouleur->update_wcd_event) { + dev_err(dev, "%s: update_wcd_event api is null!\n", + __func__); + ret = -EINVAL; + goto err_bind_all; + } + rouleur->register_notifier = plat_data->register_notifier; + if (!rouleur->register_notifier) { + dev_err(dev, "%s: register_notifier api is null!\n", + __func__); + ret = -EINVAL; + goto err_bind_all; + } + + ret = msm_cdc_enable_static_supplies(dev, rouleur->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_bind_all; + } + + rouleur_reset(dev, 0x01); + usleep_range(20, 30); + rouleur_reset(dev, 0x00); + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * as per HW requirement. + */ + usleep_range(5000, 5010); + rouleur->wakeup = rouleur_wakeup; + + ret = component_bind_all(dev, rouleur); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + goto err_bind_all; + } + + ret = rouleur_parse_port_mapping(dev, "qcom,rx_swr_ch_map", CODEC_RX); + ret |= rouleur_parse_port_mapping(dev, "qcom,tx_swr_ch_map", CODEC_TX); + + if (ret) { + dev_err(dev, "Failed to read port mapping\n"); + goto err; + } + + rouleur->rx_swr_dev = get_matching_swr_slave_device(pdata->rx_slave); + if (!rouleur->rx_swr_dev) { + dev_err(dev, "%s: Could not find RX swr slave device\n", + __func__); + ret = -ENODEV; + goto err; + } + + rouleur->tx_swr_dev = get_matching_swr_slave_device(pdata->tx_slave); + if (!rouleur->tx_swr_dev) { + dev_err(dev, "%s: Could not find TX swr slave device\n", + __func__); + ret = -ENODEV; + goto err; + } + + rouleur->regmap = devm_regmap_init_swr(rouleur->tx_swr_dev, + &rouleur_regmap_config); + if (!rouleur->regmap) { + dev_err(dev, "%s: Regmap init failed\n", + __func__); + goto err; + } + + /* Set all interupts as edge triggered */ + for (i = 0; i < rouleur_regmap_irq_chip.num_regs; i++) + regmap_write(rouleur->regmap, + (ROULEUR_DIG_SWR_INTR_LEVEL_0 + i), 0); + + rouleur_regmap_irq_chip.irq_drv_data = rouleur; + rouleur->irq_info.wcd_regmap_irq_chip = &rouleur_regmap_irq_chip; + rouleur->irq_info.codec_name = "rouleur"; + rouleur->irq_info.regmap = rouleur->regmap; + rouleur->irq_info.dev = dev; + ret = wcd_irq_init(&rouleur->irq_info, &rouleur->virq); + + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", + __func__, ret); + goto err; + } + rouleur->tx_swr_dev->slave_irq = rouleur->virq; + + mutex_init(&rouleur->micb_lock); + mutex_init(&rouleur->main_bias_lock); + mutex_init(&rouleur->rx_clk_lock); + + ret = rouleur_set_micbias_data(rouleur, pdata); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_irq; + } + + /* Request for watchdog interrupt */ + wcd_request_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHR_PDM_WD_INT, + "HPHR PDM WD INT", rouleur_wd_handle_irq, NULL); + wcd_request_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHL_PDM_WD_INT, + "HPHL PDM WD INT", rouleur_wd_handle_irq, NULL); + /* Disable watchdog interrupt for HPH */ + wcd_disable_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHR_PDM_WD_INT); + wcd_disable_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHL_PDM_WD_INT); + + ret = snd_soc_register_component(dev, &soc_codec_dev_rouleur, + NULL, 0); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_irq; + } + + return ret; +err_irq: + wcd_irq_exit(&rouleur->irq_info, rouleur->virq); + mutex_destroy(&rouleur->micb_lock); + mutex_destroy(&rouleur->main_bias_lock); + mutex_destroy(&rouleur->rx_clk_lock); +err: + component_unbind_all(dev, rouleur); +err_bind_all: + dev_set_drvdata(dev, NULL); + kfree(pdata); + kfree(rouleur); + return ret; +} + +static void rouleur_unbind(struct device *dev) +{ + struct rouleur_priv *rouleur = dev_get_drvdata(dev); + struct rouleur_pdata *pdata = dev_get_platdata(rouleur->dev); + + wcd_irq_exit(&rouleur->irq_info, rouleur->virq); + snd_soc_unregister_component(dev); + component_unbind_all(dev, rouleur); + mutex_destroy(&rouleur->micb_lock); + mutex_destroy(&rouleur->main_bias_lock); + mutex_destroy(&rouleur->rx_clk_lock); + dev_set_drvdata(dev, NULL); + kfree(pdata); + kfree(rouleur); +} + +static const struct of_device_id rouleur_dt_match[] = { + { .compatible = "qcom,rouleur-codec" }, + {} +}; + +static const struct component_master_ops rouleur_comp_ops = { + .bind = rouleur_bind, + .unbind = rouleur_unbind, +}; + +static int rouleur_compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static void rouleur_release_of(struct device *dev, void *data) +{ + of_node_put(data); +} + +static int rouleur_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, + rouleur_release_of, + rouleur_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, + rouleur_release_of, + rouleur_compare_of, + tx_node); + return 0; +} + +static int rouleur_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + int ret; + + ret = rouleur_add_slave_components(&pdev->dev, &match); + if (ret) + return ret; + + return component_master_add_with_match(&pdev->dev, + &rouleur_comp_ops, match); +} + +static int rouleur_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &rouleur_comp_ops); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops rouleur_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + rouleur_suspend, + rouleur_resume + ) +}; +#endif + +static struct platform_driver rouleur_codec_driver = { + .probe = rouleur_probe, + .remove = rouleur_remove, + .driver = { + .name = "rouleur_codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rouleur_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &rouleur_dev_pm_ops, +#endif + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(rouleur_codec_driver); +MODULE_DESCRIPTION("Rouleur Codec driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/asoc/codecs/rouleur/rouleur.h b/asoc/codecs/rouleur/rouleur.h new file mode 100644 index 00000000..51abff3a --- /dev/null +++ b/asoc/codecs/rouleur/rouleur.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _ROULEUR_H +#define _ROULEUR_H + +#ifdef CONFIG_SND_SOC_ROULEUR +extern int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_component *component); +#else +extern int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_component *component) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_ROULEUR */ + +#endif diff --git a/asoc/codecs/rouleur/rouleur_slave.c b/asoc/codecs/rouleur/rouleur_slave.c new file mode 100644 index 00000000..8fac8a6f --- /dev/null +++ b/asoc/codecs/rouleur/rouleur_slave.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/component.h> +#include <soc/soundwire.h> + +struct rouleur_slave_priv { + struct swr_device *swr_slave; +}; + +static int rouleur_slave_bind(struct device *dev, + struct device *master, void *data) +{ + int ret = 0; + struct rouleur_slave_priv *rouleur_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; + } + + rouleur_slave = devm_kzalloc(&pdev->dev, + sizeof(struct rouleur_slave_priv), GFP_KERNEL); + if (!rouleur_slave) + return -ENOMEM; + + swr_set_dev_data(pdev, rouleur_slave); + + rouleur_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 rouleur_slave_unbind(struct device *dev, + struct device *master, void *data) +{ + struct rouleur_slave_priv *rouleur_slave = NULL; + struct swr_device *pdev = to_swr_device(dev); + + if (pdev == NULL) { + dev_err(dev, "%s: pdev is NULL\n", __func__); + return; + } + + rouleur_slave = swr_get_dev_data(pdev); + if (!rouleur_slave) { + dev_err(&pdev->dev, "%s: rouleur_slave is NULL\n", __func__); + return; + } + + swr_set_dev_data(pdev, NULL); +} + +static const struct swr_device_id rouleur_swr_id[] = { + {"rouleur-slave", 0}, + {} +}; + +static const struct of_device_id rouleur_swr_dt_match[] = { + { + .compatible = "qcom,rouleur-slave", + }, + {} +}; + +static const struct component_ops rouleur_slave_comp_ops = { + .bind = rouleur_slave_bind, + .unbind = rouleur_slave_unbind, +}; + +static int rouleur_swr_up(struct swr_device *pdev) +{ + return 0; +} + +static int rouleur_swr_down(struct swr_device *pdev) +{ + return 0; +} + +static int rouleur_swr_reset(struct swr_device *pdev) +{ + return 0; +} + +static int rouleur_swr_probe(struct swr_device *pdev) +{ + return component_add(&pdev->dev, &rouleur_slave_comp_ops); +} + +static int rouleur_swr_remove(struct swr_device *pdev) +{ + component_del(&pdev->dev, &rouleur_slave_comp_ops); + return 0; +} + +static struct swr_driver rouleur_slave_driver = { + .driver = { + .name = "rouleur-slave", + .owner = THIS_MODULE, + .of_match_table = rouleur_swr_dt_match, + }, + .probe = rouleur_swr_probe, + .remove = rouleur_swr_remove, + .id_table = rouleur_swr_id, + .device_up = rouleur_swr_up, + .device_down = rouleur_swr_down, + .reset_device = rouleur_swr_reset, +}; + +static int __init rouleur_slave_init(void) +{ + return swr_driver_register(&rouleur_slave_driver); +} + +static void __exit rouleur_slave_exit(void) +{ + swr_driver_unregister(&rouleur_slave_driver); +} + +module_init(rouleur_slave_init); +module_exit(rouleur_slave_exit); + +MODULE_DESCRIPTION("WCD937X Swr Slave driver"); +MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 46a3850a..f3e71af1 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -415,7 +415,6 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) &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); @@ -424,7 +423,6 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) &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); diff --git a/asoc/codecs/wcd937x/Android.mk b/asoc/codecs/wcd937x/Android.mk index 4168dd1b..643339a1 100644 --- a/asoc/codecs/wcd937x/Android.mk +++ b/asoc/codecs/wcd937x/Android.mk @@ -13,7 +13,7 @@ endif AUDIO_CHIPSET := audio # Build/Package only in case of supported target -ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET) bengal),true) +ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET) bengal lito),true) LOCAL_PATH := $(call my-dir) diff --git a/asoc/codecs/wcd937x/Kbuild b/asoc/codecs/wcd937x/Kbuild index e353e8a3..5bbbad7c 100644 --- a/asoc/codecs/wcd937x/Kbuild +++ b/asoc/codecs/wcd937x/Kbuild @@ -31,6 +31,11 @@ ifeq ($(KERNEL_BUILD), 0) export INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.h endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif endif # As per target team, build is done as follows: diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index 2025be91..29e15dda 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -2759,6 +2759,72 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { }, { .playback = { + .stream_name = "MultiMedia24 Playback", + .aif_name = "MM_DL24", + .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 = "MultiMedia24 Capture", + .aif_name = "MM_UL24", + .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 = "MultiMedia24", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia25 Playback", + .aif_name = "MM_DL25", + .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 = "MultiMedia25 Capture", + .aif_name = "MM_UL25", + .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 = "MultiMedia25", + .probe = fe_dai_probe, + }, + { + .playback = { .stream_name = "MultiMedia26 Playback", .aif_name = "MM_DL26", .rates = (SNDRV_PCM_RATE_8000_384000| diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 24f39a60..e4e6e6f1 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -775,6 +775,12 @@ static struct msm_pcm_routing_fdai_data /* MULTIMEDIA23 */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA24 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM}, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL}, LEGACY_PCM} }, + /* MULTIMEDIA25 */ + {{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} }, @@ -9655,6 +9661,14 @@ static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { MSM_BACKEND_DAI_PRI_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { @@ -9734,6 +9748,14 @@ static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { MSM_BACKEND_DAI_PRI_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { @@ -9813,6 +9835,14 @@ static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { MSM_BACKEND_DAI_PRI_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { @@ -9892,6 +9922,14 @@ static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { MSM_BACKEND_DAI_PRI_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { @@ -9963,6 +10001,14 @@ static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = { MSM_BACKEND_DAI_PRI_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { @@ -10042,6 +10088,14 @@ static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { MSM_BACKEND_DAI_SEC_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { @@ -10121,6 +10175,14 @@ static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { MSM_BACKEND_DAI_SEC_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { @@ -10200,6 +10262,14 @@ static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { MSM_BACKEND_DAI_SEC_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { @@ -10279,6 +10349,14 @@ static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { MSM_BACKEND_DAI_SEC_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { @@ -10350,6 +10428,14 @@ static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = { MSM_BACKEND_DAI_SEC_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { @@ -10429,6 +10515,14 @@ static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { @@ -10500,6 +10594,14 @@ static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { @@ -10579,6 +10681,14 @@ static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { @@ -10658,6 +10768,14 @@ static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { @@ -10737,6 +10855,14 @@ static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { @@ -10816,6 +10942,14 @@ static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { MSM_BACKEND_DAI_TERT_TDM_RX_4, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_TERT_TDM_RX_4, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { @@ -10899,6 +11033,14 @@ static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { @@ -10970,6 +11112,14 @@ static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { @@ -11053,6 +11203,14 @@ static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { @@ -11136,6 +11294,14 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { @@ -11219,6 +11385,14 @@ static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { @@ -11302,6 +11476,14 @@ static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { MSM_BACKEND_DAI_QUIN_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_tx_0_mixer_controls[] = { @@ -11373,6 +11555,14 @@ static const struct snd_kcontrol_new quin_tdm_tx_0_mixer_controls[] = { MSM_BACKEND_DAI_QUIN_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_tx_1_mixer_controls[] = { @@ -11590,6 +11780,14 @@ static const struct snd_kcontrol_new quin_tdm_tx_2_mixer_controls[] = { MSM_BACKEND_DAI_QUIN_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = { @@ -11673,6 +11871,14 @@ static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = { MSM_BACKEND_DAI_QUIN_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_tx_3_mixer_controls[] = { @@ -11823,6 +12029,14 @@ static const struct snd_kcontrol_new quin_tdm_rx_3_mixer_controls[] = { MSM_BACKEND_DAI_QUIN_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA23, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia24", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_DOUBLE_EXT("MultiMedia25", SND_SOC_NOPM, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA25, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sen_tdm_rx_0_mixer_controls[] = { @@ -15268,6 +15482,172 @@ static const struct snd_kcontrol_new mmul23_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul24_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 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_MULTIMEDIA24, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul25_mixer_controls[] = { + SOC_DOUBLE_EXT("PRI_TDM_TX_0", SND_SOC_NOPM, + MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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_MULTIMEDIA25, 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, @@ -22272,6 +22652,7 @@ static int msm_routing_get_lsm_app_type_cfg_control( kcontrol->private_value)->shift; int i = 0, j = 0; + mutex_lock(&routing_lock); ucontrol->value.integer.value[i] = num_app_cfg_types; for (j = 0; j < num_app_cfg_types; ++j) { @@ -22285,6 +22666,7 @@ static int msm_routing_get_lsm_app_type_cfg_control( ucontrol->value.integer.value[++i] = lsm_app_type_cfg[j].num_out_channels; } + mutex_unlock(&routing_lock); return 0; } @@ -22296,9 +22678,11 @@ static int msm_routing_put_lsm_app_type_cfg_control( kcontrol->private_value)->shift; int i = 0, j; + mutex_lock(&routing_lock); if (ucontrol->value.integer.value[0] > MAX_APP_TYPES) { pr_err("%s: number of app types exceed the max supported\n", __func__); + mutex_unlock(&routing_lock); return -EINVAL; } @@ -22318,7 +22702,7 @@ static int msm_routing_put_lsm_app_type_cfg_control( lsm_app_type_cfg[j].num_out_channels = ucontrol->value.integer.value[i++]; } - + mutex_unlock(&routing_lock); return 0; } @@ -23292,6 +23676,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_IN("MM_DL21", "MultiMedia21 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL22", "MultiMedia22 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL23", "MultiMedia23 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL24", "MultiMedia24 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL25", "MultiMedia25 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL26", "MultiMedia26 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), @@ -23311,6 +23697,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_OUT("MM_UL21", "MultiMedia21 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL22", "MultiMedia22 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL23", "MultiMedia23 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL24", "MultiMedia24 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL25", "MultiMedia25 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), @@ -24418,6 +24806,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul22_mixer_controls, ARRAY_SIZE(mmul22_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia23 Mixer", SND_SOC_NOPM, 0, 0, mmul23_mixer_controls, ARRAY_SIZE(mmul23_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia24 Mixer", SND_SOC_NOPM, 0, 0, + mmul24_mixer_controls, ARRAY_SIZE(mmul24_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia25 Mixer", SND_SOC_NOPM, 0, 0, + mmul25_mixer_controls, ARRAY_SIZE(mmul25_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, @@ -25658,6 +26050,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"PRI_TDM_RX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25679,6 +26073,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25700,6 +26096,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25721,6 +26119,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Audio Mixer"}, {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25740,6 +26140,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"PRI_TDM_TX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25761,6 +26163,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"SEC_TDM_RX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25782,6 +26186,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25803,6 +26209,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25824,6 +26232,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Audio Mixer"}, {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25843,6 +26253,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"SEC_TDM_TX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25864,6 +26276,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_RX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"}, {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25883,6 +26297,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_TX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25904,6 +26320,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_RX_1 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25925,6 +26343,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_RX_2 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25946,6 +26366,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_RX_3 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25967,6 +26389,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 Audio Mixer"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -25989,6 +26413,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26008,6 +26434,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26030,6 +26458,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26052,6 +26482,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26074,6 +26506,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26096,6 +26530,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0 Audio Mixer"}, {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26115,6 +26551,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUIN_TDM_TX_0", NULL, "QUIN_TDM_TX_0 Audio Mixer"}, {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26137,6 +26575,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1 Audio Mixer"}, {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26159,6 +26599,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2 Audio Mixer"}, {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26181,6 +26623,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia23", "MM_DL23"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia24", "MM_DL24"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia25", "MM_DL25"}, {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3 Audio Mixer"}, {"SEN_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -26827,6 +27271,48 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia23 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, {"MultiMedia23 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia24 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia24 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia24 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia24 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia24 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia24 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia24 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia24 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia24 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia24 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia24 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia24 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia24 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia24 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia24 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia24 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia24 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia24 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia24 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia24 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + + {"MultiMedia25 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia25 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia25 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia25 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia25 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia25 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia25 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia25 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia25 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia25 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia25 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia25 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia25 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia25 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia25 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia25 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia25 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia25 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia25 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia25 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia27 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, {"MultiMedia27 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, @@ -27072,6 +27558,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL21", NULL, "MultiMedia21 Mixer"}, {"MM_UL22", NULL, "MultiMedia22 Mixer"}, {"MM_UL23", NULL, "MultiMedia23 Mixer"}, + {"MM_UL24", NULL, "MultiMedia24 Mixer"}, + {"MM_UL25", NULL, "MultiMedia25 Mixer"}, {"MM_UL27", NULL, "MultiMedia27 Mixer"}, {"MM_UL28", NULL, "MultiMedia28 Mixer"}, {"MM_UL29", NULL, "MultiMedia29 Mixer"}, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index ee9c63ad..6b96bdeb 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -263,6 +263,8 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA21, MSM_FRONTEND_DAI_MULTIMEDIA22, MSM_FRONTEND_DAI_MULTIMEDIA23, + MSM_FRONTEND_DAI_MULTIMEDIA24, + MSM_FRONTEND_DAI_MULTIMEDIA25, MSM_FRONTEND_DAI_MULTIMEDIA26, MSM_FRONTEND_DAI_MULTIMEDIA27, MSM_FRONTEND_DAI_MULTIMEDIA28, diff --git a/asoc/sa8155.c b/asoc/sa8155.c index 2e544470..47300605 100644 --- a/asoc/sa8155.c +++ b/asoc/sa8155.c @@ -174,14 +174,14 @@ struct tdm_conf { /* 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 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_2 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_3 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ }, { /* SEC TDM */ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* RX_0 */ @@ -228,14 +228,14 @@ static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { /* 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 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_2 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, /* TX_3 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ }, { /* SEC TDM */ {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 6}, /* TX_0 */ @@ -343,7 +343,7 @@ struct tdm_slot_cfg { static struct tdm_slot_cfg tdm_slot[TDM_INTERFACE_MAX] = { /* PRI TDM */ - {32, 8}, + {16, 16}, /* SEC TDM */ {32, 8}, /* TERT TDM */ @@ -377,11 +377,11 @@ static struct tdm_slot_cfg tdm_slot_custom[TDM_INTERFACE_MAX] = { 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 */ + {0, 0xFFFF}, + {2, 0xFFFF}, + {4, 6, 0xFFFF}, + {8, 10, 0xFFFF}, + {12, 14, 0xFFFF}, {0xFFFF}, /* not used */ {0xFFFF}, /* not used */ {0xFFFF}, /* not used */ @@ -432,11 +432,11 @@ static unsigned int tdm_rx_slot_offset 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 */ + {0, 0xFFFF}, + {2, 0xFFFF}, + {4, 6, 0xFFFF}, + {8, 10, 0xFFFF}, + {12, 14, 0xFFFF}, {0xFFFF}, /* not used */ {0xFFFF}, /* not used */ {0xFFFF}, /* not used */ @@ -5896,6 +5896,40 @@ static struct snd_soc_dai_link msm_auto_fe_dai_links[] = { .ignore_pmdown_time = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA23 }, + { + .name = MSM_DAILINK_NAME(Media24), + .stream_name = "MultiMedia24", + .cpu_dai_name = "MultiMedia24", + .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, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA24 + }, + { + .name = MSM_DAILINK_NAME(Media25), + .stream_name = "MultiMedia25", + .cpu_dai_name = "MultiMedia25", + .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, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA25 + }, }; static struct snd_soc_dai_link msm_custom_fe_dai_links[] = { |