diff options
author | Linaro CI <ci_notify@linaro.org> | 2020-08-31 00:05:07 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2020-08-31 00:05:07 +0000 |
commit | 8b283cc54d9b779973c694b64b015d84a5e0b994 (patch) | |
tree | 0933642536774a1cbdbe23bbbbc59d453a6b4775 | |
parent | 32da5becabc52dcbbb77db1b7cb48da0ac82ca1a (diff) | |
parent | 88c726a274d242f8181715545c023fc4fc6b830d (diff) |
Merge remote-tracking branch 'audio/tracking-qcomlt-audio' into integration-linux-qcomlt
30 files changed, 6446 insertions, 202 deletions
diff --git a/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt index e94a2ad3a710..1c615b622d07 100644 --- a/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt +++ b/Documentation/devicetree/bindings/slimbus/slim-ngd-qcom-ctrl.txt @@ -14,6 +14,7 @@ Please refer to slimbus/bus.txt for details of the common SLIMBus bindings. must be one of the following. "qcom,slim-ngd-v1.5.0" for MSM8996 "qcom,slim-ngd-v2.1.0" for SDM845 + "qcom,slim-ngd-v2.2.0" for SM8250 - reg: Usage: required diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 058327310c25..21784c9395b2 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -475,4 +475,12 @@ config KRAITCC Support for the Krait CPU clocks on Qualcomm devices. Say Y if you want to support CPU frequency scaling. +config CLK_GFM_LPASS + tristate "GFM LPASS Clocks" + depends on SND_SOC_QDSP6_COMMON + help + Support for the GFM Glitch Free Mux LPASS clock. Say Y + if you want to support GFM Clocks on LPASS for devices such + as SM8250 etc. + endif diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 9677e769e7e9..d918b4716d27 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -72,3 +72,4 @@ obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o obj-$(CONFIG_KRAITCC) += krait-cc.o +obj-$(CONFIG_CLK_GFM_LPASS) += lpasscc-sm8250.o diff --git a/drivers/clk/qcom/lpasscc-sm8250.c b/drivers/clk/qcom/lpasscc-sm8250.c new file mode 100644 index 000000000000..3c434b1e385e --- /dev/null +++ b/drivers/clk/qcom/lpasscc-sm8250.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <dt-bindings/clock/qcom,audiocc-sm8250.h> + +struct lpasscc_sm8250 { + struct device *dev; + void __iomem *audiocc; + void __iomem *aocc; +}; + +struct clk_gfm { + int id; + unsigned int mux_reg; + unsigned int mux_mask; + struct clk_hw hw; + struct lpasscc_sm8250 *priv; + void __iomem *gfm_mux; +}; + +#define GFM_MASK BIT(1) +#define to_clk_gfm(_hw) container_of(_hw, struct clk_gfm, hw) + +static u8 clk_gfm_get_parent(struct clk_hw *hw) +{ + struct clk_gfm *clk = to_clk_gfm(hw); + + return readl(clk->gfm_mux) & GFM_MASK; +} + +static int clk_gfm_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_gfm *clk = to_clk_gfm(hw); + unsigned int val; + + val = readl(clk->gfm_mux); + + if (index) + val |= GFM_MASK; + else + val &= ~GFM_MASK; + + writel(val, clk->gfm_mux); + + return 0; +} + +static const struct clk_ops clk_gfm_ops = { + .get_parent = clk_gfm_get_parent, + .set_parent = clk_gfm_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +static struct clk_gfm lpass_gfm_va_mclk = { + .mux_reg = 0x20000, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "VA_MCLK", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]) { + "LPASS_CLK_ID_TX_CORE_MCLK", + "LPASS_CLK_ID_VA_CORE_MCLK", + }, + .num_parents = 2, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + }, +}; + +static struct clk_gfm lpass_gfm_tx_npl = { + .mux_reg = 0x20000, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "TX_NPL", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]){ + "LPASS_CLK_ID_TX_CORE_NPL_MCLK", + "LPASS_CLK_ID_VA_CORE_2X_MCLK", + }, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + .num_parents = 2, + }, +}; + +static struct clk_gfm lpass_gfm_wsa_mclk = { + .mux_reg = 0x220d8, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "WSA_MCLK", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]){ + "LPASS_CLK_ID_TX_CORE_MCLK", + "LPASS_CLK_ID_WSA_CORE_MCLK", + }, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + .num_parents = 2, + }, +}; + +static struct clk_gfm lpass_gfm_wsa_npl = { + .mux_reg = 0x220d8, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "WSA_NPL", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]){ + "LPASS_CLK_ID_TX_CORE_NPL_MCLK", + "LPASS_CLK_ID_WSA_CORE_NPL_MCLK", + }, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + .num_parents = 2, + }, +}; + +static struct clk_gfm lpass_gfm_rx_mclk_mclk2 = { + .mux_reg = 0x240d8, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "RX_MCLK_MCLK2", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]){ + "LPASS_CLK_ID_TX_CORE_MCLK", + "LPASS_CLK_ID_RX_CORE_MCLK", + }, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + .num_parents = 2, + }, +}; + +static struct clk_gfm lpass_gfm_rx_npl = { + .mux_reg = 0x240d8, + .mux_mask = BIT(0), + .hw.init = &(struct clk_init_data) { + .name = "RX_NPL", + .ops = &clk_gfm_ops, + .flags = CLK_SET_RATE_PARENT, + .parent_names = (const char *[]){ + "LPASS_CLK_ID_TX_CORE_NPL_MCLK", + "LPASS_CLK_ID_RX_CORE_NPL_MCLK", + }, + .parent_data = (const struct clk_parent_data[]){ + { .index = 0 }, + { .index = 1 }, + }, + .num_parents = 2, + }, +}; + +static struct clk_gfm *lpass_gfm_clks[] = { + [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk, + [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl, +// [LPASS_CDC_TX_MCLK] = &lpass_gfm_tx_mclk + [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl, + [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk, +// [LPASS_CDC_RX_MCLK] = &lpass_gfm_rx_mclk_mclk2, + [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl, + [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2, +}; + +static struct clk_hw_onecell_data lpasscc_hw_onecell_data = { + .hws = { + [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk.hw, + [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl.hw, +// [LPASS_CDC_TX_MCLK] = &lpass_gfm_tx_mclk.hw, + [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl.hw, + [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk.hw, +// [LPASS_CDC_RX_MCLK] = &lpass_gfm_rx_mclk_mclk2.hw + [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl.hw, + [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2.hw, + }, + .num = ARRAY_SIZE(lpass_gfm_clks), +}; + +extern bool q6core_is_adsp_ready(void); + +static int lpasscc_sm8250_clk_driver_probe(struct platform_device *pdev) +{ + struct lpasscc_sm8250 *lpasscc; + struct device *dev = &pdev->dev; + struct clk_gfm *gfm; + struct resource *res; + struct clk *lpass_core_hw_vote = NULL; + int err, i; + + if (!q6core_is_adsp_ready()) + return -EPROBE_DEFER; + + lpass_core_hw_vote = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(lpass_core_hw_vote)) { + dev_dbg(&pdev->dev, "%s: clk get %s failed\n", + __func__, "lpass_core_hw_vote"); + return PTR_ERR(lpass_core_hw_vote); + } + + clk_prepare_enable(lpass_core_hw_vote); + + lpasscc = devm_kzalloc(dev, sizeof(*lpasscc), GFP_KERNEL); + if (!lpasscc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lpasscc->audiocc = devm_ioremap_resource(dev, res); + if (IS_ERR(lpasscc->audiocc)) + return PTR_ERR(lpasscc->audiocc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + lpasscc->aocc = devm_ioremap_resource(dev, res); + if (IS_ERR(lpasscc->aocc)) + return PTR_ERR(lpasscc->aocc); + + for (i = 0; i < ARRAY_SIZE(lpass_gfm_clks); i++) { + if (!lpass_gfm_clks[i]) + continue; + + gfm = lpass_gfm_clks[i]; + gfm->priv = lpasscc; + + if (i >= LPASS_CDC_WSA_NPL) + gfm->gfm_mux = lpasscc->audiocc; + else + gfm->gfm_mux = lpasscc->aocc; + + gfm->gfm_mux = gfm->gfm_mux + lpass_gfm_clks[i]->mux_reg; + + gfm->id = i; + err = devm_clk_hw_register(dev, &lpass_gfm_clks[i]->hw); + if (err) + return err; + + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &lpasscc_hw_onecell_data); +} + +static const struct of_device_id lpasscc_sm8250_clk_match_table[] = { + { .compatible = "qcom,sm8250-audiocc" }, + { } +}; + +static struct platform_driver lpasscc_sm8250_clk_driver = { + .probe = lpasscc_sm8250_clk_driver_probe, + .driver = { + .name = "lpass-gfm-clk", + .of_match_table = lpasscc_sm8250_clk_match_table, + }, +}; +builtin_platform_driver(lpasscc_sm8250_clk_driver); diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index f8ff30cdafa6..d58633d38303 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -227,4 +227,12 @@ config PINCTRL_SM8250 Qualcomm Technologies Inc TLMM block found on the Qualcomm Technologies Inc SM8250 platform. +config PINCTRL_LPASS + tristate "Qualcomm Technologies Inc LPASS pin controller driver" + depends on GPIOLIB && OF + help + This is the pinctrl, pinmux, pinconf and gpiolib driver for the + Qualcomm Technologies Inc LPASS (Low Power Audio SubSystem) found + on the Qualcomm Technologies Inc SoCs. + endif diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 061ec9fb659b..3a6473ad2b19 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_PINCTRL_SDM660) += pinctrl-sdm660.o obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_SM8150) += pinctrl-sm8150.o obj-$(CONFIG_PINCTRL_SM8250) += pinctrl-sm8250.o +obj-$(CONFIG_PINCTRL_LPASS) += pinctrl-lpass.o diff --git a/drivers/pinctrl/qcom/pinctrl-lpass.c b/drivers/pinctrl/qcom/pinctrl-lpass.c new file mode 100644 index 000000000000..0ba9cc570b76 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-lpass.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + */ + +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/clk.h> +#include <linux/bitops.h> + +#include "../core.h" +#include "../pinctrl-utils.h" + +#define LPI_GPIO_REG_VAL_CTL 0x00 +#define LPI_GPIO_REG_DIR_CTL 0x04 + +#define LPI_SLEW_REG_VAL_CTL 0x00 +#define LPI_SLEW_RATE_MAX 0x03 +#define LPI_SLEW_BITS_SIZE 0x02 +#define LPI_SLEW_OFFSET_INVALID 0xFFFFFFFF + +#define LPI_GPIO_REG_PULL_SHIFT 0x0 +#define LPI_GPIO_REG_PULL_MASK 0x3 + +#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2 +#define LPI_GPIO_REG_FUNCTION_MASK 0x3C + +#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6 +#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0 + +#define LPI_GPIO_REG_OE_SHIFT 0x9 +#define LPI_GPIO_REG_OE_MASK 0x200 + +#define LPI_GPIO_REG_DIR_SHIFT 0x1 +#define LPI_GPIO_REG_DIR_MASK 0x2 + +#define LPI_GPIO_BIAS_DISABLE 0x0 +#define LPI_GPIO_PULL_DOWN 0x1 +#define LPI_GPIO_KEEPER 0x2 +#define LPI_GPIO_PULL_UP 0x3 + +#define LPI_GPIO_FUNC_GPIO "gpio" +#define LPI_GPIO_FUNC_FUNC1 "func1" +#define LPI_GPIO_FUNC_FUNC2 "func2" +#define LPI_GPIO_FUNC_FUNC3 "func3" +#define LPI_GPIO_FUNC_FUNC4 "func4" +#define LPI_GPIO_FUNC_FUNC5 "func5" + +/* The index of each function in lpass_gpio_functions[] array */ +enum lpass_gpio_func_index { + LPI_GPIO_FUNC_INDEX_GPIO = 0x00, + LPI_GPIO_FUNC_INDEX_FUNC1 = 0x01, + LPI_GPIO_FUNC_INDEX_FUNC2 = 0x02, + LPI_GPIO_FUNC_INDEX_FUNC3 = 0x03, + LPI_GPIO_FUNC_INDEX_FUNC4 = 0x04, + LPI_GPIO_FUNC_INDEX_FUNC5 = 0x05, +}; + +struct lpass_pinctrl_variant_data { + int tlmm_reg_offset; + const struct pinctrl_pin_desc *pins; + int npins; + const char *const *groups; + int ngroups; + int *slew_reg_pin_offsets; +}; + +struct lpass_pinctrl { + struct device *dev; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; + struct pinctrl_desc desc; + char __iomem *tlmm_base; + char __iomem *slew_base; + +// char __iomem *base; + struct clk *lpass_core_hw_vote; + struct clk *lpass_audio_hw_vote; + struct mutex slew_access_lock; +// bool core_hw_vote_status; +// struct mutex core_hw_vote_lock; + const struct lpass_pinctrl_variant_data *data; +}; + +/* sm8250 variant specific data */ +#define SM8250_LPASS_PINS 14 +static const char *const sm8250_gpio_groups[SM8250_LPASS_PINS] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", + "gpio5", "gpio6", "gpio7", "gpio8", "gpio9", + "gpio10", "gpio11", "gpio12", "gpio13" +}; + +static const struct pinctrl_pin_desc sm8250_lpass_pins[] = { + PINCTRL_PIN(0, "gpio0"), + PINCTRL_PIN(1, "gpio1"), + PINCTRL_PIN(2, "gpio2"), + PINCTRL_PIN(3, "gpio3"), + PINCTRL_PIN(4, "gpio4"), + PINCTRL_PIN(5, "gpio5"), + PINCTRL_PIN(6, "gpio6"), + PINCTRL_PIN(7, "gpio7"), + PINCTRL_PIN(8, "gpio8"), + PINCTRL_PIN(9, "gpio9"), + PINCTRL_PIN(10, "gpio10"), + PINCTRL_PIN(11, "gpio11"), + PINCTRL_PIN(12, "gpio12"), + PINCTRL_PIN(13, "gpio13"), +}; + +static int sm8250_slew_reg_offsets[] = { + 0x0, 0x2, 0x4, 0x8, 0xa, + 0xc, 0x0, 0x0, 0x0, 0x10, + 0x12, 0x0, 0x0, 0x0 +}; + +static struct lpass_pinctrl_variant_data sm8250_lpass_data = { + .tlmm_reg_offset = 0x1000, + .pins = sm8250_lpass_pins, + .npins = ARRAY_SIZE(sm8250_lpass_pins), + .slew_reg_pin_offsets = sm8250_slew_reg_offsets, + .groups = sm8250_gpio_groups, + .ngroups = ARRAY_SIZE(sm8250_gpio_groups), +}; + +static const char *const lpass_gpio_functions[] = { + [LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO, + [LPI_GPIO_FUNC_INDEX_FUNC1] = LPI_GPIO_FUNC_FUNC1, + [LPI_GPIO_FUNC_INDEX_FUNC2] = LPI_GPIO_FUNC_FUNC2, + [LPI_GPIO_FUNC_INDEX_FUNC3] = LPI_GPIO_FUNC_FUNC3, + [LPI_GPIO_FUNC_INDEX_FUNC4] = LPI_GPIO_FUNC_FUNC4, + [LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5, +}; + +static int lpass_gpio_read(struct lpass_pinctrl *state, unsigned int pin, + unsigned int addr) +{ + return ioread32(state->tlmm_base + + state->data->tlmm_reg_offset * pin + addr); +} + +static int lpass_gpio_write(struct lpass_pinctrl *state, unsigned int pin, + unsigned int addr, unsigned int val) +{ + iowrite32(val, state->tlmm_base + + state->data->tlmm_reg_offset * pin + addr); + return 0; +} + +static int lpass_gpio_get_groups_count(struct pinctrl_dev *pctldev) +{ + /* Every PIN is a group */ + return pctldev->desc->npins; +} + +static const char *lpass_gpio_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + struct lpass_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->data->groups[pin]; +} + +static int lpass_gpio_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int pin, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &pctldev->desc->pins[pin].number; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops lpass_gpio_pinctrl_ops = { + .get_groups_count = lpass_gpio_get_groups_count, + .get_group_name = lpass_gpio_get_group_name, + .get_group_pins = lpass_gpio_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int lpass_gpio_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(lpass_gpio_functions); +} + +static const char *lpass_gpio_get_function_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return lpass_gpio_functions[function]; +} + +static int lpass_gpio_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char *const **groups, + unsigned *const num_qgroups) +{ + struct lpass_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctrl->data->groups; + *num_qgroups = pctrl->data->ngroups; + + return 0; +} + +static int lpass_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, + unsigned int pin) +{ + struct lpass_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + unsigned int val; + + val = lpass_gpio_read(pctrl, pin, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_FUNCTION_MASK); + val |= function << LPI_GPIO_REG_FUNCTION_SHIFT; + lpass_gpio_write(pctrl, pin, LPI_GPIO_REG_VAL_CTL, val); + + return 0; +} + +static const struct pinmux_ops lpass_gpio_pinmux_ops = { + .get_functions_count = lpass_gpio_get_functions_count, + .get_function_name = lpass_gpio_get_function_name, + .get_function_groups = lpass_gpio_get_function_groups, + .set_mux = lpass_gpio_set_mux, +}; + +static int lpass_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct lpass_pinctrl *state = dev_get_drvdata(pctldev->dev); + unsigned int arg = 0; + int is_out; + int pull; + u32 ctl_reg; + + ctl_reg = lpass_gpio_read(state, pin, LPI_GPIO_REG_DIR_CTL); + + is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT; + + ctl_reg = lpass_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL); + + pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (pull == LPI_GPIO_BIAS_DISABLE) + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (pull == LPI_GPIO_PULL_DOWN) + arg = 1; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + if (pull == LPI_GPIO_KEEPER) + arg = 1; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (pull == LPI_GPIO_PULL_UP) + arg = 1; + break; + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + if (is_out) + arg = 1; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static unsigned int lpass_drive_to_regval(u32 arg) +{ + return (arg/2 - 1); +} + +static int lpass_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + unsigned int param, arg; + int i, ret = 0; + volatile unsigned long val; + struct lpass_pinctrl *state = dev_get_drvdata(pctldev->dev); + bool output_enabled; + unsigned int pullup; + unsigned int strength; + unsigned int offset; + bool value; + + for (i = 0; i < nconfs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + pullup = LPI_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pullup = LPI_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + pullup = LPI_GPIO_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pullup = LPI_GPIO_PULL_UP; + break; + case PIN_CONFIG_INPUT_ENABLE: + output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + output_enabled = true; + lpass_gpio_write(state, pin, LPI_GPIO_REG_DIR_CTL, + output_enabled << LPI_GPIO_REG_DIR_SHIFT); + value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + strength = arg; + break; + case PIN_CONFIG_SLEW_RATE: + if (arg > LPI_SLEW_RATE_MAX) { + dev_err(pctldev->dev, "%s: invalid slew rate %u for pin: %d\n", + __func__, arg, pin); + goto set_gpio; + } + + mutex_lock(&state->slew_access_lock); + + val = lpass_gpio_read(state, pin, LPI_SLEW_REG_VAL_CTL); + + offset = state->data->slew_reg_pin_offsets[pin];//pad->slew_offset; + for (i = 0; i < LPI_SLEW_BITS_SIZE; i++) { + if (arg & 0x01) + set_bit(offset, &val); + else + clear_bit(offset, &val); + offset++; + arg = arg >> 1; + } + lpass_gpio_write(state, pin, LPI_SLEW_REG_VAL_CTL, val); + + mutex_unlock(&state->slew_access_lock); + break; + default: + ret = -EINVAL; + goto done; + } + } + +set_gpio: + + val = lpass_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK | + LPI_GPIO_REG_OE_MASK); + val |= pullup << LPI_GPIO_REG_PULL_SHIFT; + val |= lpass_drive_to_regval(strength) << LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + if (output_enabled) + val |= value << LPI_GPIO_REG_OE_SHIFT; + + lpass_gpio_write(state, pin, LPI_GPIO_REG_VAL_CTL, val); + lpass_gpio_write(state, pin, LPI_GPIO_REG_DIR_CTL, + output_enabled << LPI_GPIO_REG_DIR_SHIFT); +done: + return ret; +} + +static const struct pinconf_ops lpass_gpio_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = lpass_config_get, + .pin_config_group_set = lpass_config_set, +}; + +static int lpass_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct lpass_pinctrl *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return lpass_config_set(state->ctrl, pin, &config, 1); +} + +static int lpass_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct lpass_pinctrl *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return lpass_config_set(state->ctrl, pin, &config, 1); +} + +static int lpass_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct lpass_pinctrl *state = gpiochip_get_data(chip); + int value; + + value = lpass_gpio_read(state, pin, LPI_GPIO_REG_VAL_CTL); + return value; +} + +static void lpass_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct lpass_pinctrl *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + lpass_config_set(state->ctrl, pin, &config, 1); +} +#ifdef CONFIG_DEBUG_FS +#include <linux/seq_file.h> + +static unsigned int lpass_regval_to_drive(u32 val) +{ + return (val + 1) * 2; +} + +static void lpass_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, + struct gpio_chip *chip, + unsigned int offset, + unsigned int gpio) +{ + struct lpass_pinctrl *state = gpiochip_get_data(chip); + struct pinctrl_pin_desc pindesc; + unsigned int func; + int is_out; + int drive; + int pull; + u32 ctl_reg; + + static const char * const pulls[] = { + "no pull", + "pull down", + "keeper", + "pull up" + }; + + pctldev = pctldev ? : state->ctrl; + pindesc = pctldev->desc->pins[offset]; + ctl_reg = lpass_gpio_read(state, offset, LPI_GPIO_REG_DIR_CTL); + is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT; + ctl_reg = lpass_gpio_read(state, offset, LPI_GPIO_REG_VAL_CTL); + + func = (ctl_reg & LPI_GPIO_REG_FUNCTION_MASK) >> + LPI_GPIO_REG_FUNCTION_SHIFT; + drive = (ctl_reg & LPI_GPIO_REG_OUT_STRENGTH_MASK) >> + LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT; + + seq_printf(s, " %-8s: %-3s %d", + pindesc.name, is_out ? "out" : "in", func); + seq_printf(s, " %dmA", lpass_regval_to_drive(drive)); + seq_printf(s, " %s", pulls[pull]); +} + +static void lpass_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned int gpio = chip->base; + unsigned int i; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + lpass_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } +} + +#else +#define lpass_gpio_dbg_show NULL +#endif + +static const struct gpio_chip lpass_gpio_template = { + .direction_input = lpass_gpio_direction_input, + .direction_output = lpass_gpio_direction_output, + .get = lpass_gpio_get, + .set = lpass_gpio_set, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .dbg_show = lpass_gpio_dbg_show, +}; + +static int lpass_pinctrl_probe(struct platform_device *pdev) +{ + int ret, npins; + struct clk *lpass_core_hw_vote = NULL; + struct clk *lpass_audio_hw_vote = NULL; + + struct lpass_pinctrl *pctrl; + const struct lpass_pinctrl_variant_data *data; + struct device *dev = &pdev->dev; + struct resource *res; + + pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + platform_set_drvdata(pdev, pctrl); + + data = of_device_get_match_data(dev); + pctrl->data = data; + pctrl->dev = &pdev->dev; + npins = data->npins; + + /* Register LPASS core hw vote */ + lpass_core_hw_vote = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(lpass_core_hw_vote)) { + dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "lpass_core_hw_vote", ret); + return PTR_ERR(lpass_core_hw_vote); + } + pctrl->lpass_core_hw_vote = lpass_core_hw_vote; + + /* Register LPASS audio hw vote */ + lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "audio"); + if (IS_ERR(lpass_audio_hw_vote)) { + dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "lpass_audio_hw_vote", ret); + return PTR_ERR(lpass_audio_hw_vote); + } + + pctrl->lpass_audio_hw_vote = lpass_audio_hw_vote; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pctrl->tlmm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctrl->tlmm_base)) + return PTR_ERR(pctrl->tlmm_base); + + clk_prepare_enable(pctrl->lpass_core_hw_vote); + clk_prepare_enable(pctrl->lpass_audio_hw_vote); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pctrl->slew_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctrl->slew_base)) + return PTR_ERR(pctrl->slew_base); + + pctrl->desc.pctlops = &lpass_gpio_pinctrl_ops; + pctrl->desc.pmxops = &lpass_gpio_pinmux_ops; + pctrl->desc.confops = &lpass_gpio_pinconf_ops; + + pctrl->desc.owner = THIS_MODULE; + pctrl->desc.name = dev_name(dev); + pctrl->desc.pins = data->pins; + pctrl->desc.npins = data->npins; + + pctrl->chip = lpass_gpio_template; + pctrl->chip.parent = dev; + pctrl->chip.base = -1; + pctrl->chip.ngpio = npins; + pctrl->chip.label = dev_name(dev); + pctrl->chip.of_gpio_n_cells = 2; + pctrl->chip.can_sleep = false; + + mutex_init(&pctrl->slew_access_lock); +// mutex_init(&pctrl->core_hw_vote_lock); + + pctrl->ctrl = devm_pinctrl_register(dev, &pctrl->desc, pctrl); + if (IS_ERR(pctrl->ctrl)) + return PTR_ERR(pctrl->ctrl); + + ret = gpiochip_add_data(&pctrl->chip, pctrl); + if (ret) { + dev_err(pctrl->dev, "can't add gpio chip\n"); + goto err_chip; + } + + ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(dev), 0, 0, npins); + if (ret) { + dev_err(dev, "failed to add pin range\n"); + goto err_range; + } + + return 0; + +err_range: + gpiochip_remove(&pctrl->chip); +err_chip: +// mutex_destroy(&pctrl->core_hw_vote_lock); + mutex_destroy(&pctrl->slew_access_lock); + return ret; +} + +static int lpass_pinctrl_remove(struct platform_device *pdev) +{ + struct lpass_pinctrl *state = platform_get_drvdata(pdev); + + gpiochip_remove(&state->chip); +// mutex_destroy(&state->core_hw_vote_lock); + mutex_destroy(&state->slew_access_lock); + + return 0; +} + +static const struct of_device_id lpass_pinctrl_of_match[] = { + { + .compatible = "qcom,sm8250-lpass-pinctrl", + .data = &sm8250_lpass_data, + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, lpass_pinctrl_of_match); + +static struct platform_driver lpass_pinctrl_driver = { + .driver = { + .name = "qcom-lpass-pinctrl", + .of_match_table = lpass_pinctrl_of_match, + }, + .probe = lpass_pinctrl_probe, + .remove = lpass_pinctrl_remove, +}; + +module_platform_driver(lpass_pinctrl_driver); + +MODULE_DESCRIPTION("QTI LPI GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 743ee7b4e63f..e643338678a6 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1323,6 +1323,9 @@ static const struct of_device_id qcom_slim_ngd_dt_match[] = { },{ .compatible = "qcom,slim-ngd-v2.1.0", .data = &ngd_v1_5_offset_info, + },{ + .compatible = "qcom,slim-ngd-v2.2.0", + .data = &ngd_v1_5_offset_info, }, {} }; diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index fa2b4ab92ed9..d121cf739090 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -33,7 +33,6 @@ config SOUNDWIRE_INTEL config SOUNDWIRE_QCOM tristate "Qualcomm SoundWire Master driver" - depends on SLIMBUS depends on SND_SOC help SoundWire Qualcomm Master driver. diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 915c2cf0c274..4b3ef7559e6a 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -34,6 +34,7 @@ #define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED BIT(10) #define SWRM_INTERRUPT_MASK_ADDR 0x204 #define SWRM_INTERRUPT_CLEAR 0x208 +#define SWRM_INTERRUPT_CPU_EN 0x210 #define SWRM_CMD_FIFO_WR_CMD 0x300 #define SWRM_CMD_FIFO_RD_CMD 0x304 #define SWRM_CMD_FIFO_CMD 0x308 @@ -90,6 +91,7 @@ struct qcom_swrm_ctrl { struct sdw_bus bus; struct device *dev; struct regmap *regmap; + void __iomem *mmio; struct completion *comp; struct work_struct slave_work; /* read/write lock */ @@ -114,7 +116,7 @@ struct qcom_swrm_ctrl { #define to_qcom_sdw(b) container_of(b, struct qcom_swrm_ctrl, bus) -static int qcom_swrm_abh_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, +static int qcom_swrm_ahb_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, u32 *val) { struct regmap *wcd_regmap = ctrl->regmap; @@ -154,6 +156,20 @@ static int qcom_swrm_ahb_reg_write(struct qcom_swrm_ctrl *ctrl, return SDW_CMD_OK; } +static int qcom_swrm_cpu_reg_read(struct qcom_swrm_ctrl *ctrl, int reg, + u32 *val) +{ + *val = readl(ctrl->mmio + reg); + return SDW_CMD_OK; +} + +static int qcom_swrm_cpu_reg_write(struct qcom_swrm_ctrl *ctrl, int reg, + int val) +{ + writel(val, ctrl->mmio + reg); + return SDW_CMD_OK; +} + static int qcom_swrm_cmd_fifo_wr_cmd(struct qcom_swrm_ctrl *ctrl, u8 cmd_data, u8 dev_addr, u16 reg_addr) { @@ -310,6 +326,12 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl) ctrl->reg_write(ctrl, SWRM_COMP_CFG_ADDR, SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_MSK | SWRM_COMP_CFG_ENABLE_MSK); + + /* enable CPU IRQs */ + if (ctrl->mmio) { + ctrl->reg_write(ctrl, SWRM_INTERRUPT_CPU_EN, + SWRM_INTERRUPT_STATUS_RMSK); + } return 0; } @@ -746,6 +768,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) struct sdw_master_prop *prop; struct sdw_bus_params *params; struct qcom_swrm_ctrl *ctrl; + struct resource *res; int ret; u32 val; @@ -753,15 +776,25 @@ static int qcom_swrm_probe(struct platform_device *pdev) if (!ctrl) return -ENOMEM; +#ifdef CONFIG_SLIMBUS if (dev->parent->bus == &slimbus_bus) { - ctrl->reg_read = qcom_swrm_abh_reg_read; +#else + if (false) { +#endif + ctrl->reg_read = qcom_swrm_ahb_reg_read; ctrl->reg_write = qcom_swrm_ahb_reg_write; ctrl->regmap = dev_get_regmap(dev->parent, NULL); if (!ctrl->regmap) return -EINVAL; } else { - /* Only WCD based SoundWire controller is supported */ - return -ENOTSUPP; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ctrl->reg_read = qcom_swrm_cpu_reg_read; + ctrl->reg_write = qcom_swrm_cpu_reg_write; + ctrl->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->mmio)) + return PTR_ERR(ctrl->mmio); } ctrl->irq = of_irq_get(dev->of_node, 0); @@ -859,6 +892,7 @@ static int qcom_swrm_remove(struct platform_device *pdev) static const struct of_device_id qcom_swrm_of_match[] = { { .compatible = "qcom,soundwire-v1.3.0", }, + { .compatible = "qcom,soundwire-v1.5.1", }, {/* sentinel */}, }; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 37290a799023..56e87137c1dd 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -704,9 +704,12 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) } if (!multi_link) { - kfree(wr_msg); - kfree(wbuf); - bus->defer_msg.msg = NULL; + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } + bus->params.curr_bank = !bus->params.curr_bank; bus->params.next_bank = !bus->params.next_bank; } @@ -716,7 +719,11 @@ static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) error: kfree(wbuf); error_1: - kfree(wr_msg); + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } + return ret; } @@ -749,6 +756,7 @@ static int sdw_ml_sync_bank_switch(struct sdw_bus *bus) if (bus->defer_msg.msg) { kfree(bus->defer_msg.msg->buf); kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; } return 0; @@ -840,9 +848,11 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) error: list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; - - kfree(bus->defer_msg.msg->buf); - kfree(bus->defer_msg.msg); + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + bus->defer_msg.msg = NULL; + } } msg_unlock: diff --git a/include/dt-bindings/clock/qcom,audiocc-sm8250.h b/include/dt-bindings/clock/qcom,audiocc-sm8250.h new file mode 100644 index 000000000000..5a8918e84351 --- /dev/null +++ b/include/dt-bindings/clock/qcom,audiocc-sm8250.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DT_BINDINGS_CLK_LPASS_SM8250_H +#define _DT_BINDINGS_CLK_LPASS_SM8250_H + +/* from AOCC */ +#define LPASS_CDC_VA_MCLK 0 +#define LPASS_CDC_TX_NPL 1 +#define LPASS_CDC_TX_MCLK 2 +/* From AudioCC */ +#define LPASS_CDC_WSA_NPL 3 +#define LPASS_CDC_WSA_MCLK 4 +#define LPASS_CDC_RX_MCLK 5 +#define LPASS_CDC_RX_NPL 6 +#define LPASS_CDC_RX_MCLK_MCLK2 7 + +#endif /* _DT_BINDINGS_CLK_LPASS_SM8250_H */ diff --git a/include/dt-bindings/sound/qcom,q6afe.h b/include/dt-bindings/sound/qcom,q6afe.h index 1df06f8ad5c3..f64b5d2e6efd 100644 --- a/include/dt-bindings/sound/qcom,q6afe.h +++ b/include/dt-bindings/sound/qcom,q6afe.h @@ -107,6 +107,100 @@ #define QUINARY_TDM_RX_7 102 #define QUINARY_TDM_TX_7 103 #define DISPLAY_PORT_RX 104 +#define WSA_CODEC_DMA_RX_0 105 +#define WSA_CODEC_DMA_TX_0 106 +#define WSA_CODEC_DMA_RX_1 107 +#define WSA_CODEC_DMA_TX_1 108 +#define WSA_CODEC_DMA_TX_2 109 +#define VA_CODEC_DMA_TX_0 110 +#define VA_CODEC_DMA_TX_1 111 +#define VA_CODEC_DMA_TX_2 112 +#define RX_CODEC_DMA_RX_0 113 +#define TX_CODEC_DMA_TX_0 114 +#define RX_CODEC_DMA_RX_1 115 +#define TX_CODEC_DMA_TX_1 116 +#define RX_CODEC_DMA_RX_2 117 +#define TX_CODEC_DMA_TX_2 118 +#define RX_CODEC_DMA_RX_3 119 +#define TX_CODEC_DMA_TX_3 120 +#define RX_CODEC_DMA_RX_4 121 +#define TX_CODEC_DMA_TX_4 122 +#define RX_CODEC_DMA_RX_5 123 +#define TX_CODEC_DMA_TX_5 124 +#define RX_CODEC_DMA_RX_6 125 +#define RX_CODEC_DMA_RX_7 126 -#endif /* __DT_BINDINGS_Q6_AFE_H__ */ +#define LPASS_CLK_ID_PRI_MI2S_IBIT 1 +#define LPASS_CLK_ID_PRI_MI2S_EBIT 2 +#define LPASS_CLK_ID_SEC_MI2S_IBIT 3 +#define LPASS_CLK_ID_SEC_MI2S_EBIT 4 +#define LPASS_CLK_ID_TER_MI2S_IBIT 5 +#define LPASS_CLK_ID_TER_MI2S_EBIT 6 +#define LPASS_CLK_ID_QUAD_MI2S_IBIT 7 +#define LPASS_CLK_ID_QUAD_MI2S_EBIT 8 +#define LPASS_CLK_ID_SPEAKER_I2S_IBIT 9 +#define LPASS_CLK_ID_SPEAKER_I2S_EBIT 10 +#define LPASS_CLK_ID_SPEAKER_I2S_OSR 11 +#define LPASS_CLK_ID_QUI_MI2S_IBIT 12 +#define LPASS_CLK_ID_QUI_MI2S_EBIT 13 +#define LPASS_CLK_ID_SEN_MI2S_IBIT 14 +#define LPASS_CLK_ID_SEN_MI2S_EBIT 15 +#define LPASS_CLK_ID_INT0_MI2S_IBIT 16 +#define LPASS_CLK_ID_INT1_MI2S_IBIT 17 +#define LPASS_CLK_ID_INT2_MI2S_IBIT 18 +#define LPASS_CLK_ID_INT3_MI2S_IBIT 19 +#define LPASS_CLK_ID_INT4_MI2S_IBIT 20 +#define LPASS_CLK_ID_INT5_MI2S_IBIT 21 +#define LPASS_CLK_ID_INT6_MI2S_IBIT 22 +#define LPASS_CLK_ID_QUI_MI2S_OSR 23 +#define LPASS_CLK_ID_PRI_PCM_IBIT 24 +#define LPASS_CLK_ID_PRI_PCM_EBIT 25 +#define LPASS_CLK_ID_SEC_PCM_IBIT 26 +#define LPASS_CLK_ID_SEC_PCM_EBIT 27 +#define LPASS_CLK_ID_TER_PCM_IBIT 28 +#define LPASS_CLK_ID_TER_PCM_EBIT 29 +#define LPASS_CLK_ID_QUAD_PCM_IBIT 30 +#define LPASS_CLK_ID_QUAD_PCM_EBIT 31 +#define LPASS_CLK_ID_QUIN_PCM_IBIT 32 +#define LPASS_CLK_ID_QUIN_PCM_EBIT 33 +#define LPASS_CLK_ID_QUI_PCM_OSR 34 +#define LPASS_CLK_ID_PRI_TDM_IBIT 35 +#define LPASS_CLK_ID_PRI_TDM_EBIT 36 +#define LPASS_CLK_ID_SEC_TDM_IBIT 37 +#define LPASS_CLK_ID_SEC_TDM_EBIT 38 +#define LPASS_CLK_ID_TER_TDM_IBIT 39 +#define LPASS_CLK_ID_TER_TDM_EBIT 40 +#define LPASS_CLK_ID_QUAD_TDM_IBIT 41 +#define LPASS_CLK_ID_QUAD_TDM_EBIT 42 +#define LPASS_CLK_ID_QUIN_TDM_IBIT 43 +#define LPASS_CLK_ID_QUIN_TDM_EBIT 44 +#define LPASS_CLK_ID_QUIN_TDM_OSR 45 +#define LPASS_CLK_ID_MCLK_1 46 +#define LPASS_CLK_ID_MCLK_2 47 +#define LPASS_CLK_ID_MCLK_3 48 +#define LPASS_CLK_ID_MCLK_4 49 +#define LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 50 +#define LPASS_CLK_ID_INT_MCLK_0 51 +#define LPASS_CLK_ID_INT_MCLK_1 52 +#define LPASS_CLK_ID_MCLK_5 53 +#define LPASS_CLK_ID_WSA_CORE_MCLK 54 +#define LPASS_CLK_ID_WSA_CORE_NPL_MCLK 55 +#define LPASS_CLK_ID_VA_CORE_MCLK 56 +#define LPASS_CLK_ID_TX_CORE_MCLK 57 +#define LPASS_CLK_ID_TX_CORE_NPL_MCLK 58 +#define LPASS_CLK_ID_RX_CORE_MCLK 59 +#define LPASS_CLK_ID_RX_CORE_NPL_MCLK 60 +#define LPASS_CLK_ID_VA_CORE_2X_MCLK 61 + +#define LPASS_HW_AVTIMER_VOTE 101 +#define LPASS_HW_MACRO_VOTE 102 +#define LPASS_HW_DCODEC_VOTE 103 + +#define Q6AFE_MAX_CLK_ID 104 +#define LPASS_CLK_ATTRIBUTE_INVALID 0x0 +#define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 +#define LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3 + +#endif /* __DT_BINDINGS_Q6_AFE_H__ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 946a70210f49..86e576d091e6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1757,4 +1757,7 @@ config SND_SOC_TPA6130A2 tristate "Texas Instruments TPA6130A2 headphone amplifier" depends on I2C +config SND_SOC_LPASS_WSA_MACRO + tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" + endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0140c60db695..0e0f8079a94a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -102,6 +102,7 @@ snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-lochnagar-sc-objs := lochnagar-sc.o +snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o snd-soc-madera-objs := madera.o snd-soc-max9759-objs := max9759.o snd-soc-max9768-objs := max9768.o @@ -605,3 +606,4 @@ obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c new file mode 100644 index 000000000000..8471d19bc6c8 --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -0,0 +1,3026 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of_clk.h> +#include <linux/clk-provider.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/of_platform.h> +#include <sound/tlv.h> +#include <linux/pinctrl/consumer.h> + +#include "lpass-wsa-macro.h" + +#define AUTO_SUSPEND_DELAY 50 /* delay in msec */ +#define WSA_MACRO_MAX_OFFSET 0x1000 + +#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define NUM_INTERPOLATORS 2 + +#define WSA_MACRO_MUX_INP_SHFT 0x3 +#define WSA_MACRO_MUX_INP_MASK1 0x07 +#define WSA_MACRO_MUX_INP_MASK2 0x38 +#define WSA_MACRO_MUX_CFG_OFFSET 0x8 +#define WSA_MACRO_MUX_CFG1_OFFSET 0x4 +#define WSA_MACRO_RX_COMP_OFFSET 0x40 +#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 +#define WSA_MACRO_RX_PATH_OFFSET 0x80 +#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 +#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C +#define WSA_MACRO_FS_RATE_MASK 0x0F +#define WSA_MACRO_EC_MIX_TX0_MASK 0x03 +#define WSA_MACRO_EC_MIX_TX1_MASK 0x18 + +#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2 + +enum { + WSA_MACRO_RX0 = 0, + WSA_MACRO_RX1, + WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX1, + WSA_MACRO_RX_MAX, +}; + +enum { + WSA_MACRO_TX0 = 0, + WSA_MACRO_TX1, + WSA_MACRO_TX_MAX, +}; + +enum { + WSA_MACRO_EC0_MUX = 0, + WSA_MACRO_EC1_MUX, + WSA_MACRO_EC_MUX_MAX, +}; + +enum { + WSA_MACRO_COMP1, /* SPK_L */ + WSA_MACRO_COMP2, /* SPK_R */ + WSA_MACRO_COMP_MAX +}; + +enum { + WSA_MACRO_SOFTCLIP0, /* RX0 */ + WSA_MACRO_SOFTCLIP1, /* RX1 */ + WSA_MACRO_SOFTCLIP_MAX +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct wsa_macro_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +#define WSA_MACRO_SWR_STRING_LEN 80 + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); +static int wsa_macro_digital_mute(struct snd_soc_dai *dai, int mute, int + stream); +/* Hold instance to soundwire platform device */ +struct wsa_macro_swr_ctrl_data { + struct platform_device *wsa_swr_pdev; +}; + +struct wsa_macro_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*core_vote)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +struct wsa_macro_bcl_pmic_params { + u8 id; + u8 sid; + u8 ppid; +}; + +enum { + WSA_MACRO_AIF_INVALID = 0, + WSA_MACRO_AIF1_PB, + WSA_MACRO_AIF_MIX1_PB, + WSA_MACRO_AIF_VI, + WSA_MACRO_AIF_ECHO, + WSA_MACRO_MAX_DAIS, +}; + +#define WSA_MACRO_CHILD_DEVICES_MAX 3 + +/* + * @dev: wsa macro device pointer + * @comp_enabled: compander enable mixer value set + * @ec_hq: echo HQ enable mixer value set + * @prim_int_users: Users of interpolator + * @wsa_mclk_users: WSA MCLK users count + * @swr_clk_users: SWR clk users count + * @vi_feed_value: VI sense mask + * @mclk_lock: to lock mclk operations + * @swr_clk_lock: to lock swr master clock operations + * @swr_ctrl_data: SoundWire data structure + * @swr_plat_data: Soundwire platform data + * @wsa_macro_add_child_devices_work: work for adding child devices + * @wsa_swr_gpio_p: used by pinctrl API + * @component: codec handle + * @rx_0_count: RX0 interpolation users + * @rx_1_count: RX1 interpolation users + * @active_ch_mask: channel mask for all AIF DAIs + * @active_ch_cnt: channel count of all AIF DAIs + * @rx_port_value: mixer ctl value of WSA RX MUXes + */ +struct wsa_macro_priv { + struct device *dev; + int comp_enabled[WSA_MACRO_COMP_MAX]; + int ec_hq[WSA_MACRO_RX1 + 1]; + u16 prim_int_users[WSA_MACRO_RX1 + 1]; + u16 wsa_mclk_users; + u16 swr_clk_users; + bool dapm_mclk_enable; + bool reset_swr; + unsigned int vi_feed_value; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + //struct wsa_macro_swr_ctrl_data *swr_ctrl_data; + //struct wsa_macro_swr_ctrl_platform_data swr_plat_data; + //struct work_struct wsa_macro_add_child_devices_work; + // struct device_node *wsa_swr_gpio_p; + //struct snd_soc_component *component; + int rx_0_count; + int rx_1_count; + unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; + int rx_port_value[WSA_MACRO_RX_MAX]; + //struct platform_device *pdev_child_devices + // [WSA_MACRO_CHILD_DEVICES_MAX]; + //int child_count; + int ear_spkr_gain; + int spkr_gain_offset; + int spkr_mode; + int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; + int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; + struct wsa_macro_bcl_pmic_params bcl_pmic_params; + // char __iomem *mclk_mode_muxsel; + // u16 default_clk_id; + int wsa_digital_mute_status[WSA_MACRO_RX_MAX]; + + struct regmap *regmap; + struct clk *hw_vote; + struct clk *dcodec_vote; + struct clk *clk; + struct clk *npl_clk; + struct clk_hw hw; +}; +#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro_priv, hw) + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component, + struct wsa_macro_priv *wsa_priv, + int event, int gain_reg); +static struct snd_soc_dai_driver wsa_macro_dai[]; +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static const char *const rx_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" +}; + +static const char *const rx_mix_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" +}; + +static const char *const rx_mix_ec_text[] = { + "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" +}; + +static const char *const rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF_MIX1_PB" +}; + +static const char *const rx_sidetone_mix_text[] = { + "ZERO", "SRC0" +}; + +static const char * const wsa_macro_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static const char * const wsa_macro_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + +static const char * const wsa_macro_vbat_bcl_gsm_mode_text[] = { + "OFF", "ON" +}; + +static const struct snd_kcontrol_new wsa_int0_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("WSA RX0 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new wsa_int1_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("WSA RX1 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_spkr_boost_stage_enum, + wsa_macro_speaker_boost_stage_text); +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_vbat_bcl_gsm_mode_enum, + wsa_macro_vbat_bcl_gsm_mode_text); + +/* RX INT0 */ +static const struct soc_enum rx0_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx0_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx0_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx0_mix_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 5, rx_mix_text); + +static const struct soc_enum rx0_sidetone_mix_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); + +static const struct snd_kcontrol_new rx0_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx0_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); + +static const struct snd_kcontrol_new rx0_sidetone_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); + +/* RX INT1 */ +static const struct soc_enum rx1_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx1_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx1_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx1_mix_chain_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 5, rx_mix_text); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx1_mix_mux = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); + +static const struct soc_enum rx_mix_ec0_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0, 3, rx_mix_ec_text); + +static const struct soc_enum rx_mix_ec1_enum = + SOC_ENUM_SINGLE(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 3, 3, rx_mix_ec_text); + +static const struct snd_kcontrol_new rx_mix_ec0_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum); + +static const struct snd_kcontrol_new rx_mix_ec1_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum); + +static struct snd_soc_dai_ops wsa_macro_dai_ops = { + .hw_params = wsa_macro_hw_params, + .get_channel_map = wsa_macro_get_channel_map, + .mute_stream = wsa_macro_digital_mute, +}; + +static struct snd_soc_dai_driver wsa_macro_dai[] = { + { + .name = "wsa_macro_rx1", + .id = WSA_MACRO_AIF1_PB, + .playback = { + .stream_name = "WSA_AIF1 Playback", + .rates = WSA_MACRO_RX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_rx_mix", + .id = WSA_MACRO_AIF_MIX1_PB, + .playback = { + .stream_name = "WSA_AIF_MIX1 Playback", + .rates = WSA_MACRO_RX_MIX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 192000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_vifeedback", + .id = WSA_MACRO_AIF_VI, + .capture = { + .stream_name = "WSA_AIF_VI Capture", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_echo", + .id = WSA_MACRO_AIF_ECHO, + .capture = { + .stream_name = "WSA_AIF_ECHO Capture", + .rates = WSA_MACRO_ECHO_RATES, + .formats = WSA_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_spkr_default[] = { + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58}, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_spkr_mode1[] = { + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/** + * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @component: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode) +{ + int i; + const struct wsa_macro_reg_mask_val *regs; + int size; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + switch (mode) { + case WSA_MACRO_SPKR_MODE_1: + regs = wsa_macro_spkr_mode1; + size = ARRAY_SIZE(wsa_macro_spkr_mode1); + break; + default: + regs = wsa_macro_spkr_default; + size = ARRAY_SIZE(wsa_macro_spkr_default); + break; + } + + wsa_priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_component_update_bits(component, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(wsa_macro_set_spkr_mode); + +static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j, port; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_component *component = dai->component; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa_priv->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < WSA_MACRO_RX0) || + (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, + "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the cdc_dma rx port + * is connected + */ + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; + + int_mux_cfg0_val = snd_soc_component_read(component, + int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, + int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & WSA_MACRO_MUX_INP_MASK1; + inp1_sel = (int_mux_cfg0_val >> + WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK1; + inp2_sel = (int_mux_cfg1_val >> + WSA_MACRO_MUX_INP_SHFT) & + WSA_MACRO_MUX_INP_MASK1; + if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { + int_fs_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + dev_dbg(component->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(component->dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + snd_soc_component_update_bits(component, + int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + + return 0; +} + +static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j, port; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_component *component = dai->component; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa_priv->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + int_2_inp = port; + if ((int_2_inp < WSA_MACRO_RX0) || + (int_2_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, + "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_component_read(component, + int_mux_cfg1) & + WSA_MACRO_MUX_INP_MASK1; + if (int_mux_cfg1_val == int_2_inp + + INTn_2_INP_SEL_RX0) { + int_fs_reg = + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + + dev_dbg(component->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(component->dev, + "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_component_update_bits(component, + int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + return 0; +} + +static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == + int_mix_sample_rate_val[i].sample_rate) { + rate_val = + int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || + (rate_val < 0)) + goto prim_rate; + ret = wsa_macro_set_mix_interpolator_rate(dai, + (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == + int_prim_sample_rate_val[i].sample_rate) { + rate_val = + int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || + (rate_val < 0)) + return -EINVAL; + ret = wsa_macro_set_prim_interpolator_rate(dai, + (u8) rate_val, sample_rate); + return ret; +} + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + dev_dbg(component->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wsa_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(component->dev, + "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + default: + break; + } + return 0; +} + +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + u16 val = 0, mask = 0, cnt = 0, temp = 0; + + wsa_priv = dev_get_drvdata(component->dev); + if (!wsa_priv) + return -EINVAL; + + switch (dai->id) { + case WSA_MACRO_AIF_VI: + *tx_slot = wsa_priv->active_ch_mask[dai->id]; + *tx_num = wsa_priv->active_ch_cnt[dai->id]; + break; + case WSA_MACRO_AIF1_PB: + case WSA_MACRO_AIF_MIX1_PB: + for_each_set_bit(temp, &wsa_priv->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + mask |= (1 << temp); + if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + if (mask & 0x0C) + mask = mask >> 0x2; + *rx_slot = mask; + *rx_num = cnt; + break; + case WSA_MACRO_AIF_ECHO: + val = snd_soc_component_read(component, + BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + if (val & WSA_MACRO_EC_MIX_TX1_MASK) { + mask |= 0x2; + cnt++; + } + if (val & WSA_MACRO_EC_MIX_TX0_MASK) { + mask |= 0x1; + cnt++; + } + *tx_slot = mask; + *tx_num = cnt; + break; + default: + dev_err(component->dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int wsa_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + uint16_t j = 0, reg = 0, mix_reg = 0, dsm_reg = 0; + u16 int_mux_cfg0 = 0, int_mux_cfg1 = 0; + u8 int_mux_cfg0_val = 0, int_mux_cfg1_val = 0; + + if (mute) + return 0; + + switch (dai->id) { + case WSA_MACRO_AIF1_PB: + case WSA_MACRO_AIF_MIX1_PB: + for (j = 0; j < NUM_INTERPOLATORS; j++) { + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL + + (j * WSA_MACRO_RX_PATH_OFFSET); + mix_reg = BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL + + (j * WSA_MACRO_RX_PATH_OFFSET); + dsm_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL + + (j * WSA_MACRO_RX_PATH_OFFSET) + + WSA_MACRO_RX_PATH_DSMDEM_OFFSET; + int_mux_cfg0 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + j * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + int_mux_cfg0_val = snd_soc_component_read(component, + int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, + int_mux_cfg1); + if (snd_soc_component_read(component, dsm_reg) & 0x01) { + if (int_mux_cfg0_val || (int_mux_cfg1_val & 0x38)) + snd_soc_component_update_bits(component, reg, + 0x20, 0x20); + if (int_mux_cfg1_val & 0x07) { + snd_soc_component_update_bits(component, reg, + 0x20, 0x20); + snd_soc_component_update_bits(component, + mix_reg, 0x20, 0x20); + } + } + } + //bolero_wsa_pa_on(component->dev); + break; + default: + break; + } + return 0; +} +static int wsa_macro_mclk_enable(struct wsa_macro_priv *wsa_priv, + bool mclk_enable, bool dapm) +{ + struct regmap *regmap = wsa_priv->regmap; + int ret = 0; + + if (regmap == NULL) { + dev_err(wsa_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + dev_err(wsa_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, wsa_priv->wsa_mclk_users); + + mutex_lock(&wsa_priv->mclk_lock); + if (mclk_enable) { + if (wsa_priv->wsa_mclk_users == 0) { +#if 0 + ret = bolero_clk_rsc_request_clock(wsa_priv->dev, + wsa_priv->default_clk_id, + wsa_priv->default_clk_id, + true); + if (ret < 0) { + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa request clock enable failed\n", + __func__); + goto exit; + } + bolero_clk_rsc_fs_gen_request(wsa_priv->dev, + true); +#endif + pr_err("DEBUG: %s: %d: \n", __func__, __LINE__); + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + WSA_START_OFFSET, + WSA_MAX_OFFSET); + pr_err("DEBUG: %s: %d: \n", __func__, __LINE__); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, + BOLERO_CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + pr_err("DEBUG: %s: %d: \n", __func__, __LINE__); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + pr_err("DEBUG: %s: %d: \n", __func__, __LINE__); + } + wsa_priv->wsa_mclk_users++; + } else { + if (wsa_priv->wsa_mclk_users <= 0) { + dev_err(wsa_priv->dev, "%s: clock already disabled\n", + __func__); + wsa_priv->wsa_mclk_users = 0; + goto exit; + } + wsa_priv->wsa_mclk_users--; + if (wsa_priv->wsa_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); +#if 0 + bolero_clk_rsc_fs_gen_request(wsa_priv->dev, + false); + + bolero_clk_rsc_request_clock(wsa_priv->dev, + wsa_priv->default_clk_id, + wsa_priv->default_clk_id, + false); +#endif + } + } +exit: + mutex_unlock(&wsa_priv->mclk_lock); + return ret; +} + +static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int ret = 0; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = wsa_macro_mclk_enable(wsa_priv, 1, true); + if (ret) + wsa_priv->dapm_mclk_enable = false; + else + wsa_priv->dapm_mclk_enable = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa_priv->dapm_mclk_enable) + wsa_macro_mclk_enable(wsa_priv, 0, true); + break; + default: + dev_err(wsa_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int wsa_macro_enable_vi_feedback(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 wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component);; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + dev_dbg(component->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + } + if (test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + dev_dbg(component->dev, "%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x0F, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x10, 0x10); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + /* Disable V&I sensing */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + dev_dbg(component->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + } + if (test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + /* Disable V&I sensing */ + dev_dbg(component->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x20, 0x20); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, + 0x10, 0x00); + } + break; + } + + return 0; +} + +static int wsa_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + dev_dbg(component->dev, "%s %d %s\n", __func__, event, w->name); + + switch (w->reg) { + case BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL: + gain_reg = BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL; + break; + case BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL: + gain_reg = BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL; + break; + default: + dev_err(component->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + w->reg, 0x20, 0x00); + break; + } + + return 0; +} + +static void wsa_macro_hd2_control(struct snd_soc_component *component, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (reg == BOLERO_CDC_WSA_RX0_RX_PATH_CTL) { + hd2_scale_reg = BOLERO_CDC_WSA_RX0_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG0; + } + if (reg == BOLERO_CDC_WSA_RX1_RX_PATH_CTL) { + hd2_scale_reg = BOLERO_CDC_WSA_RX1_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_WSA_RX1_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + 0x3C, 0x10); + snd_soc_component_update_bits(component, hd2_scale_reg, + 0x03, 0x01); + snd_soc_component_update_bits(component, hd2_enable_reg, + 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + 0x04, 0x00); + snd_soc_component_update_bits(component, hd2_scale_reg, + 0x03, 0x00); + snd_soc_component_update_bits(component, hd2_scale_reg, + 0x3C, 0x00); + } +} + +static int wsa_macro_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int ch_cnt; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!(strnstr(w->name, "RX0", sizeof("WSA_RX0"))) && + !wsa_priv->rx_0_count) + wsa_priv->rx_0_count++; + if (!(strnstr(w->name, "RX1", sizeof("WSA_RX1"))) && + !wsa_priv->rx_1_count) + wsa_priv->rx_1_count++; + ch_cnt = wsa_priv->rx_0_count + wsa_priv->rx_1_count; + +#if 0 + if (wsa_priv->swr_ctrl_data) { + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } +#endif + break; + case SND_SOC_DAPM_POST_PMD: + if (!(strnstr(w->name, "RX0", sizeof("WSA_RX0"))) && + wsa_priv->rx_0_count) + wsa_priv->rx_0_count--; + if (!(strnstr(w->name, "RX1", sizeof("WSA_RX1"))) && + wsa_priv->rx_1_count) + wsa_priv->rx_1_count--; + ch_cnt = wsa_priv->rx_0_count + wsa_priv->rx_1_count; + +#if 0 + if (wsa_priv->swr_ctrl_data) + swrm_wcd_notify( + wsa_priv->swr_ctrl_data[0].wsa_swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); +#endif + break; + } + dev_dbg(wsa_priv->dev, "%s: current swr ch cnt: %d\n", + __func__, wsa_priv->rx_0_count + wsa_priv->rx_1_count); + + return 0; +} + +static int wsa_macro_config_compander(struct snd_soc_component *component, + int comp, int event) +{ + u16 comp_ctl0_reg, rx_path_cfg0_reg; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, wsa_priv->comp_enabled[comp]); + + if (!wsa_priv->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = BOLERO_CDC_WSA_COMPANDER0_CTL0 + + (comp * WSA_MACRO_RX_COMP_OFFSET); + rx_path_cfg0_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG0 + + (comp * WSA_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x01, 0x01); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x02, 0x02); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x02, 0x00); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x04, 0x04); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + 0x02, 0x00); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x02, 0x02); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x02, 0x00); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x01, 0x00); + snd_soc_component_update_bits(component, comp_ctl0_reg, + 0x04, 0x00); + } + + return 0; +} + +static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, + struct wsa_macro_priv *wsa_priv, + int path, + bool enable) +{ + u16 softclip_clk_reg = BOLERO_CDC_WSA_SOFTCLIP0_CRC + + (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u8 softclip_mux_mask = (1 << path); + u8 softclip_mux_value = (1 << path); + + dev_dbg(component->dev, "%s: path %d, enable %d\n", + __func__, path, enable); + if (enable) { + if (wsa_priv->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, 0x01, 0x01); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, softclip_mux_value); + } + wsa_priv->softclip_clk_users[path]++; + } else { + wsa_priv->softclip_clk_users[path]--; + if (wsa_priv->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, 0x01, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, 0x00); + } + } +} + +static int wsa_macro_config_softclip(struct snd_soc_component *component, + int path, int event) +{ + u16 softclip_ctrl_reg = 0; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + int softclip_path = 0; + + if (path == WSA_MACRO_COMP1) + softclip_path = WSA_MACRO_SOFTCLIP0; + else if (path == WSA_MACRO_COMP2) + softclip_path = WSA_MACRO_SOFTCLIP1; + + dev_dbg(component->dev, "%s: event %d path %d, enabled %d\n", + __func__, event, softclip_path, + wsa_priv->is_softclip_on[softclip_path]); + + if (!wsa_priv->is_softclip_on[softclip_path]) + return 0; + + softclip_ctrl_reg = BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + + (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock and mux */ + wsa_macro_enable_softclip_clk(component, wsa_priv, + softclip_path, true); + /* Enable Softclip control */ + snd_soc_component_update_bits(component, softclip_ctrl_reg, + 0x01, 0x01); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, softclip_ctrl_reg, + 0x01, 0x00); + wsa_macro_enable_softclip_clk(component, wsa_priv, + softclip_path, false); + } + + return 0; +} + +static bool wsa_macro_adie_lb(struct snd_soc_component *component, + int interp_idx) +{ + u16 int_mux_cfg0 = 0, int_mux_cfg1 = 0; + u8 int_mux_cfg0_val = 0, int_mux_cfg1_val = 0; + u8 int_n_inp0 = 0, int_n_inp1 = 0, int_n_inp2 = 0; + + int_mux_cfg0 = BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1); + + int_n_inp0 = int_mux_cfg0_val & 0x0F; + if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || + int_n_inp0 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp1 = int_mux_cfg0_val >> 4; + if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || + int_n_inp1 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp2 = int_mux_cfg1_val >> 4; + if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || + int_n_inp2 == INTn_1_INP_SEL_DEC1) + return true; + + return false; +} + +static int wsa_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + u16 reg = 0; + + + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL + + WSA_MACRO_RX_PATH_OFFSET * w->shift; + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wsa_macro_adie_lb(component, w->shift)) { + snd_soc_component_update_bits(component, + reg, 0x20, 0x20); + //bolero_wsa_pa_on(component->dev); + } + break; + default: + break; + } + return 0; +} + +static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case BOLERO_CDC_WSA_RX0_RX_PATH_CTL: + case BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL: + prim_int_reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + *ind = 0; + break; + case BOLERO_CDC_WSA_RX1_RX_PATH_CTL: + case BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL: + prim_int_reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + *ind = 1; + break; + } + + return prim_int_reg; +} + +static int wsa_macro_enable_prim_interpolator( + struct snd_soc_component *component, + u16 reg, int event) +{ + u16 prim_int_reg; + u16 ind = 0; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component);; + + prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa_priv->prim_int_users[ind]++; + if (wsa_priv->prim_int_users[ind] == 1) { + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET, + 0x03, 0x03); + snd_soc_component_update_bits(component, prim_int_reg, + 0x10, 0x10); + wsa_macro_hd2_control(component, prim_int_reg, event); + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + 0x1, 0x1); + } + if ((reg != prim_int_reg) && + ((snd_soc_component_read( + component, prim_int_reg)) & 0x10)) + snd_soc_component_update_bits(component, reg, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_priv->prim_int_users[ind]--; + if (wsa_priv->prim_int_users[ind] == 0) { + snd_soc_component_update_bits(component, prim_int_reg, + 1 << 0x5, 0 << 0x5); + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + 0x1, 0x0); + snd_soc_component_update_bits(component, prim_int_reg, + 0x40, 0x40); + snd_soc_component_update_bits(component, prim_int_reg, + 0x40, 0x00); + wsa_macro_hd2_control(component, prim_int_reg, event); + } + break; + } + + dev_dbg(component->dev, "%s: primary interpolator: INT%d, users: %d\n", + __func__, ind, wsa_priv->prim_int_users[ind]); + return 0; +} + +static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "WSA_RX INT0 INTERP"))) { + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + gain_reg = BOLERO_CDC_WSA_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "WSA_RX INT1 INTERP"))) { + reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + gain_reg = BOLERO_CDC_WSA_RX1_RX_VOL_CTL; + } else { + dev_err(component->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wsa_macro_enable_prim_interpolator(component, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + /* apply gain after int clk is enabled */ + if ((wsa_priv->spkr_gain_offset == + WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa_priv->comp_enabled[WSA_MACRO_COMP1] || + wsa_priv->comp_enabled[WSA_MACRO_COMP2]) && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL || + gain_reg == BOLERO_CDC_WSA_RX1_RX_VOL_CTL)) { + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + wsa_macro_config_ear_spkr_gain(component, wsa_priv, + event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + wsa_macro_enable_prim_interpolator(component, reg, event); + if ((wsa_priv->spkr_gain_offset == + WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa_priv->comp_enabled[WSA_MACRO_COMP1] || + wsa_priv->comp_enabled[WSA_MACRO_COMP2]) && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL || + gain_reg == BOLERO_CDC_WSA_RX1_RX_VOL_CTL)) { + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + } + wsa_macro_config_ear_spkr_gain(component, wsa_priv, + event, gain_reg); + break; + } + + return 0; +} + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component, + struct wsa_macro_priv *wsa_priv, + int event, int gain_reg) +{ + int comp_gain_offset, val; + + switch (wsa_priv->spkr_mode) { + /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */ + case WSA_MACRO_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (wsa_priv->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL) && + (wsa_priv->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + wsa_priv->ear_spkr_gain - 1; + snd_soc_component_write(component, gain_reg, val); + + dev_dbg(wsa_priv->dev, "%s: RX0 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX0 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (wsa_priv->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == BOLERO_CDC_WSA_RX0_RX_VOL_CTL) && + (wsa_priv->ear_spkr_gain != 0)) { + snd_soc_component_write(component, gain_reg, 0x0); + + dev_dbg(wsa_priv->dev, "%s: Reset RX0 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int wsa_macro_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(component->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "WSA_RX INT0 CHAIN")) { + boost_path_ctl = BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = BOLERO_CDC_WSA_RX0_RX_PATH_CFG1; + reg = BOLERO_CDC_WSA_RX0_RX_PATH_CTL; + reg_mix = BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "WSA_RX INT1 CHAIN")) { + boost_path_ctl = BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = BOLERO_CDC_WSA_RX1_RX_PATH_CFG1; + reg = BOLERO_CDC_WSA_RX1_RX_PATH_CTL; + reg_mix = BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL; + } else { + dev_err(component->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, boost_path_cfg1, + 0x01, 0x01); + snd_soc_component_update_bits(component, boost_path_ctl, + 0x10, 0x10); + if ((snd_soc_component_read(component, reg_mix)) & 0x10) + snd_soc_component_update_bits(component, reg_mix, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, boost_path_ctl, + 0x10, 0x00); + snd_soc_component_update_bits(component, boost_path_cfg1, + 0x01, 0x00); + break; + } + + return 0; +} + + +static int wsa_macro_enable_vbat(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + u16 vbat_path_cfg = 0; + int softclip_path = 0; + + dev_dbg(component->dev, "%s %s %d\n", __func__, w->name, event); + if (!strcmp(w->name, "WSA_RX INT0 VBAT")) { + vbat_path_cfg = BOLERO_CDC_WSA_RX0_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP0; + } else if (!strcmp(w->name, "WSA_RX INT1 VBAT")) { + vbat_path_cfg = BOLERO_CDC_WSA_RX1_RX_PATH_CFG1; + softclip_path = WSA_MACRO_SOFTCLIP1; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable clock for VBAT block */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x10); + /* Enable VBAT block */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x01); + /* Update interpolator with 384K path */ + snd_soc_component_update_bits(component, vbat_path_cfg, + 0x80, 0x80); + /* Use attenuation mode */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x02, 0x00); + /* + * BCL block needs softclip clock and mux config to be enabled + */ + wsa_macro_enable_softclip_clk(component, wsa_priv, + softclip_path, true); + /* Enable VBAT at channel level */ + snd_soc_component_update_bits(component, vbat_path_cfg, + 0x02, 0x02); + /* Set the ATTK1 gain */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0xFF); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x03); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + /* Set the ATTK2 gain */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0xFF); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x03); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + /* Set the ATTK3 gain */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0xFF); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x03); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, vbat_path_cfg, + 0x80, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x02, 0x02); + snd_soc_component_update_bits(component, vbat_path_cfg, + 0x02, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, + 0xFF, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, + 0xFF, 0x00); + wsa_macro_enable_softclip_clk(component, wsa_priv, + softclip_path, false); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x01, 0x00); + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x10, 0x00); + break; + default: + dev_err(component->dev, "%s: Invalid event %d\n", __func__, event); + break; + } + return 0; +} + +static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + u16 val, ec_tx = 0, ec_hq_reg; + + dev_dbg(component->dev, "%s %d %s\n", __func__, event, w->name); + + val = snd_soc_component_read(component, + BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + if (!(strcmp(w->name, "WSA RX_MIX EC0_MUX"))) + ec_tx = (val & 0x07) - 1; + else + ec_tx = ((val & 0x38) >> 0x3) - 1; + + if (ec_tx < 0 || ec_tx >= (WSA_MACRO_RX1 + 1)) { + dev_err(component->dev, "%s: EC mix control not set correctly\n", + __func__); + return -EINVAL; + } + if (wsa_priv->ec_hq[ec_tx]) { + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0x1 << ec_tx, 0x1 << ec_tx); + ec_hq_reg = BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + + 0x40 * ec_tx; + snd_soc_component_update_bits(component, ec_hq_reg, 0x01, 0x01); + ec_hq_reg = BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + + 0x40 * ec_tx; + /* default set to 48k */ + snd_soc_component_update_bits(component, ec_hq_reg, 0x1E, 0x08); + } + + return 0; +} + +static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa_priv->ec_hq[ec_tx]; + return 0; +} + +static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: enable current %d, new %d\n", + __func__, wsa_priv->ec_hq[ec_tx], value); + wsa_priv->ec_hq[ec_tx] = value; + + return 0; +} + +static int wsa_macro_get_rx_mute_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + int wsa_rx_shift = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + wsa_priv->wsa_digital_mute_status[wsa_rx_shift]; + return 0; +} + +static int wsa_macro_set_rx_mute_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + int value = ucontrol->value.integer.value[0]; + int wsa_rx_shift = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + + + switch (wsa_rx_shift) { + case 0: + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_CTL, + 0x10, value << 4); + break; + case 1: + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_CTL, + 0x10, value << 4); + break; + case 2: + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL, + 0x10, value << 4); + break; + case 3: + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL, + 0x10, value << 4); + break; + default: + pr_err("%s: invalid argument rx_shift = %d\n", __func__, + wsa_rx_shift); + return -EINVAL; + } + + dev_dbg(component->dev, "%s: WSA Digital Mute RX %d Enable %d\n", + __func__, wsa_rx_shift, value); + wsa_priv->wsa_digital_mute_status[wsa_rx_shift] = value; + return 0; +} + +static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa_priv->comp_enabled[comp]; + return 0; +} + +static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + + dev_dbg(component->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, wsa_priv->comp_enabled[comp], value); + wsa_priv->comp_enabled[comp] = value; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa_priv->ear_spkr_gain; + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + wsa_priv->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(component->dev, "%s: gain = %d\n", __func__, + wsa_priv->ear_spkr_gain); + + return 0; +} + +static int wsa_macro_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + bst_state_max = snd_soc_component_read(component, + BOLERO_CDC_WSA_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + /* bolero does not need to limit the boost levels */ + + return 0; +} + +static int wsa_macro_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + bst_state_max = snd_soc_component_read(component, + BOLERO_CDC_WSA_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + /* bolero does not need to limit the boost levels */ + + return 0; +} + +static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = + wsa_priv->rx_port_value[widget->shift]; + return 0; +} + +static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 bit_input = 0; + u32 aif_rst; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + aif_rst = wsa_priv->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) { + dev_err(component->dev, "%s: AIF reset already\n", __func__); + return 0; + } + if (aif_rst >= WSA_MACRO_RX_MAX) { + dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); + return 0; + } + } + wsa_priv->rx_port_value[widget->shift] = rx_port_value; + + bit_input = widget->shift; + + dev_dbg(component->dev, + "%s: mux input: %d, mux output: %d, bit: %d\n", + __func__, rx_port_value, widget->shift, bit_input); + + switch (rx_port_value) { + case 0: + if (wsa_priv->active_ch_cnt[aif_rst]) { + clear_bit(bit_input, + &wsa_priv->active_ch_mask[aif_rst]); + wsa_priv->active_ch_cnt[aif_rst]--; + } + break; + case 1: + case 2: + set_bit(bit_input, + &wsa_priv->active_ch_mask[rx_port_value]); + wsa_priv->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(component->dev, + "%s: Invalid AIF_ID for WSA RX MUX %d\n", + __func__, rx_port_value); + return -EINVAL; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int wsa_macro_vbat_bcl_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_component_read( + component, BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(component->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_vbat_bcl_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + dev_dbg(component->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + + + ucontrol->value.integer.value[0] = wsa_priv->is_softclip_on[path]; + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + + wsa_priv->is_softclip_on[path] = ucontrol->value.integer.value[0]; + + dev_dbg(component->dev, "%s: soft clip enable for %d: %d\n", __func__, + path, wsa_priv->is_softclip_on[path]); + + return 0; +} + +static const struct snd_kcontrol_new wsa_macro_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_get, + wsa_macro_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", + wsa_macro_spkr_boost_stage_enum, + wsa_macro_spkr_left_boost_stage_get, + wsa_macro_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", + wsa_macro_spkr_boost_stage_enum, + wsa_macro_spkr_right_boost_stage_get, + wsa_macro_spkr_right_boost_stage_put), + SOC_ENUM_EXT("GSM mode Enable", wsa_macro_vbat_bcl_gsm_mode_enum, + wsa_macro_vbat_bcl_gsm_mode_func_get, + wsa_macro_vbat_bcl_gsm_mode_func_put), + SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP0, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP1, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_SX_TLV("WSA_RX0 Digital Volume", + BOLERO_CDC_WSA_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("WSA_RX1 Digital Volume", + BOLERO_CDC_WSA_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_EXT("WSA_RX0 Digital Mute", SND_SOC_NOPM, WSA_MACRO_RX0, 1, + 0, wsa_macro_get_rx_mute_status, + wsa_macro_set_rx_mute_status), + SOC_SINGLE_EXT("WSA_RX1 Digital Mute", SND_SOC_NOPM, WSA_MACRO_RX1, 1, + 0, wsa_macro_get_rx_mute_status, + wsa_macro_set_rx_mute_status), + SOC_SINGLE_EXT("WSA_RX0_MIX Digital Mute", SND_SOC_NOPM, + WSA_MACRO_RX_MIX0, 1, 0, wsa_macro_get_rx_mute_status, + wsa_macro_set_rx_mute_status), + SOC_SINGLE_EXT("WSA_RX1_MIX Digital Mute", SND_SOC_NOPM, + WSA_MACRO_RX_MIX1, 1, 0, wsa_macro_get_rx_mute_status, + wsa_macro_set_rx_mute_status), + SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, + 1, 0, wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), + SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, + 1, 0, wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), +}; + +static const struct soc_enum rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text); + +static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = { + SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), +}; + +static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = + ((struct soc_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 spk_tx_id = mixer->shift; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + if (test_bit(spk_tx_id, &wsa_priv->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = + ((struct soc_mixer_control *)kcontrol->private_value); + u32 spk_tx_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(component); + + wsa_priv->vi_feed_value = ucontrol->value.integer.value[0]; + + if (enable) { + if (spk_tx_id == WSA_MACRO_TX0 && + !test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + if (spk_tx_id == WSA_MACRO_TX1 && + !test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + } else { + if (spk_tx_id == WSA_MACRO_TX0 && + test_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX0, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + if (spk_tx_id == WSA_MACRO_TX1 && + test_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX1, + &wsa_priv->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa_priv->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static const struct snd_kcontrol_new aif_vi_mixer[] = { + SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), + SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0, + wsa_macro_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI, + 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM, + WSA_MACRO_EC0_MUX, 0, + &rx_mix_ec0_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM, + WSA_MACRO_EC1_MUX, 0, + &rx_mix_ec1_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0, + &rx_mux[WSA_MACRO_RX0]), + SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0, + &rx_mux[WSA_MACRO_RX1]), + SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0, + &rx_mux[WSA_MACRO_RX_MIX0]), + SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0, + &rx_mux[WSA_MACRO_RX_MIX1]), + + SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp0_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp1_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, + &rx0_prim_inp2_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL, + 0, 0, &rx0_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp0_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp1_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, + &rx1_prim_inp2_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL, + 0, 0, &rx1_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, + 0, 0, NULL, 0, wsa_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, + 1, 0, NULL, 0, wsa_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("WSA_RX0 INT0 SIDETONE MIX", + BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 4, 0, + &rx0_sidetone_mix_mux, wsa_macro_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("WSA SRC0_INP"), + + SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP1, 0, NULL, 0, wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP2, 0, NULL, 0, wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 VBAT", SND_SOC_NOPM, + 0, 0, wsa_int0_vbat_mix_switch, + ARRAY_SIZE(wsa_int0_vbat_mix_switch), + wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 VBAT", SND_SOC_NOPM, + 0, 0, wsa_int1_vbat_mix_switch, + ARRAY_SIZE(wsa_int1_vbat_mix_switch), + wsa_macro_enable_vbat, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_WSA"), + + SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0, + wsa_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wsa_audio_map[] = { + /* VI Feedback */ + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"}, + {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"}, + {"WSA AIF_VI", NULL, "WSA_MCLK"}, + + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA_MCLK"}, + + {"WSA AIF1 PB", NULL, "WSA_MCLK"}, + {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"}, + + {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + + {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + + {"WSA RX0", NULL, "WSA RX0 MUX"}, + {"WSA RX1", NULL, "WSA RX1 MUX"}, + {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"}, + {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"}, + + {"WSA_RX0 INP0", "RX0", "WSA RX0"}, + {"WSA_RX0 INP0", "RX1", "WSA RX1"}, + {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"}, + + {"WSA_RX0 INP1", "RX0", "WSA RX0"}, + {"WSA_RX0 INP1", "RX1", "WSA RX1"}, + {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"}, + + {"WSA_RX0 INP2", "RX0", "WSA RX0"}, + {"WSA_RX0 INP2", "RX1", "WSA RX1"}, + {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"}, + + {"WSA_RX0 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX0 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"}, + + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"}, + {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"}, + + {"WSA_RX INT0 VBAT", "WSA RX0 VBAT Enable", "WSA_RX INT0 INTERP"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 VBAT"}, + + {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"}, + {"WSA_SPK1 OUT", NULL, "WSA_MCLK"}, + + {"WSA_RX1 INP0", "RX0", "WSA RX0"}, + {"WSA_RX1 INP0", "RX1", "WSA RX1"}, + {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"}, + + {"WSA_RX1 INP1", "RX0", "WSA RX0"}, + {"WSA_RX1 INP1", "RX1", "WSA RX1"}, + {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"}, + + {"WSA_RX1 INP2", "RX0", "WSA RX0"}, + {"WSA_RX1 INP2", "RX1", "WSA RX1"}, + {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"}, + + {"WSA_RX1 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX1 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"}, + + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"}, + {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"}, + + {"WSA_RX INT1 VBAT", "WSA RX1 VBAT Enable", "WSA_RX INT1 INTERP"}, + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 VBAT"}, + + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"}, + {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"}, + {"WSA_SPK2 OUT", NULL, "WSA_MCLK"}, +}; + +static const struct wsa_macro_reg_mask_val wsa_macro_reg_init[] = { + {BOLERO_CDC_WSA_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x1E, 0x18}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x1E, 0x18}, + {BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0x70, 0x58}, + {BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0x70, 0x58}, + {BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 0x08, 0x08}, + {BOLERO_CDC_WSA_RX1_RX_PATH_CFG1, 0x08, 0x08}, + {BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x02, 0x02}, + {BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80}, + {BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, +}; + +static void wsa_macro_init_bcl_pmic_reg(struct snd_soc_component *component) +{ + struct wsa_macro_priv *wsa_priv = NULL; + + if (!component) { + pr_err("%s: NULL component pointer!\n", __func__); + return; + } + + wsa_priv = snd_soc_component_get_drvdata(component); + + switch (wsa_priv->bcl_pmic_params.id) { + case 0: + /* Enable ID0 to listen to respective PMIC group interrupts */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x02, 0x02); + /* Update MC_SID0 */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1, 0x0F, + wsa_priv->bcl_pmic_params.sid); + /* Update MC_PPID0 */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2, 0xFF, + wsa_priv->bcl_pmic_params.ppid); + break; + case 1: + /* Enable ID1 to listen to respective PMIC group interrupts */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0x01, 0x01); + /* Update MC_SID1 */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3, 0x0F, + wsa_priv->bcl_pmic_params.sid); + /* Update MC_PPID1 */ + snd_soc_component_update_bits(component, + BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4, 0xFF, + wsa_priv->bcl_pmic_params.ppid); + break; + default: + dev_err(component->dev, "%s: PMIC ID is invalid %d\n", + __func__, wsa_priv->bcl_pmic_params.id); + break; + } +} + +static void wsa_macro_init_reg(struct snd_soc_component *component) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wsa_macro_reg_init); i++) + snd_soc_component_update_bits(component, + wsa_macro_reg_init[i].reg, + wsa_macro_reg_init[i].mask, + wsa_macro_reg_init[i].val); + + wsa_macro_init_bcl_pmic_reg(component); +} + +static int wsa_swrm_clock(void *handle, bool enable) +{ + struct wsa_macro_priv *wsa_priv = (struct wsa_macro_priv *) handle; + struct regmap *regmap = wsa_priv->regmap; + int ret = 0; + + if (regmap == NULL) { + dev_err(wsa_priv->dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&wsa_priv->swr_clk_lock); + + trace_printk("%s: %s swrm clock %s\n", + dev_name(wsa_priv->dev), __func__, + (enable ? "enable" : "disable")); + dev_dbg(wsa_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + //pm_runtime_get_sync(wsa_priv->dev); + if (wsa_priv->swr_clk_users == 0) { + ret = 0;//msm_cdc_pinctrl_select_active_state( + // wsa_priv->wsa_swr_gpio_p); + if (ret < 0) { + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa swr pinctrl enable failed\n", + __func__); + pm_runtime_mark_last_busy(wsa_priv->dev); + pm_runtime_put_autosuspend(wsa_priv->dev); + goto exit; + } + ret = wsa_macro_mclk_enable(wsa_priv, 1, true); + if (ret < 0) { + //msm_cdc_pinctrl_select_sleep_state( + // wsa_priv->wsa_swr_gpio_p); + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa request clock enable failed\n", + __func__); + pm_runtime_mark_last_busy(wsa_priv->dev); + pm_runtime_put_autosuspend(wsa_priv->dev); + goto exit; + } + if (wsa_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x02); + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + if (wsa_priv->reset_swr) + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x02, 0x00); + wsa_priv->reset_swr = false; + } + wsa_priv->swr_clk_users++; + //pm_runtime_mark_last_busy(wsa_priv->dev); + //pm_runtime_put_autosuspend(wsa_priv->dev); + } else { +#if 0 + if (wsa_priv->swr_clk_users <= 0) { + dev_err(wsa_priv->dev, "%s: clock already disabled\n", + __func__); + wsa_priv->swr_clk_users = 0; + goto exit; + } + wsa_priv->swr_clk_users--; + if (wsa_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + wsa_macro_mclk_enable(wsa_priv, 0, true); + ret = 0;//msm_cdc_pinctrl_select_sleep_state( + // wsa_priv->wsa_swr_gpio_p); + if (ret < 0) { + dev_err_ratelimited(wsa_priv->dev, + "%s: wsa swr pinctrl disable failed\n", + __func__); + goto exit; + } + } +#endif + } + trace_printk("%s: %s swrm clock users: %d\n", + dev_name(wsa_priv->dev), __func__, + wsa_priv->swr_clk_users); + dev_dbg(wsa_priv->dev, "%s: swrm clock users %d\n", + __func__, wsa_priv->swr_clk_users); +exit: + mutex_unlock(&wsa_priv->swr_clk_lock); + return ret; +} + +#if 0 +static void wsa_macro_add_child_devices(struct work_struct *work) +{ + struct wsa_macro_priv *wsa_priv; + struct platform_device *pdev; + struct device_node *node; + struct wsa_macro_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret; + u16 count = 0, ctrl_num = 0; + struct wsa_macro_swr_ctrl_platform_data *platdata; + char plat_dev_name[WSA_MACRO_SWR_STRING_LEN]; + + wsa_priv = container_of(work, struct wsa_macro_priv, + wsa_macro_add_child_devices_work); + if (!wsa_priv) { + pr_err("%s: Memory for wsa_priv does not exist\n", + __func__); + return; + } + if (!wsa_priv->dev || !wsa_priv->dev->of_node) { + dev_err(wsa_priv->dev, + "%s: DT node for wsa_priv does not exist\n", __func__); + return; + } + + platdata = &wsa_priv->swr_plat_data; + wsa_priv->child_count = 0; + + for_each_available_child_of_node(wsa_priv->dev->of_node, node) { + if (strnstr(node->name, "wsa_swr_master", + strlen("wsa_swr_master")) != NULL) + strlcpy(plat_dev_name, "wsa_swr_ctrl", + (WSA_MACRO_SWR_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WSA_MACRO_SWR_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wsa_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = wsa_priv->dev; + pdev->dev.of_node = node; + + if (strnstr(node->name, "wsa_swr_master", + strlen("wsa_swr_master")) != NULL) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "wsa_swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct wsa_macro_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].wsa_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + wsa_priv->swr_ctrl_data = swr_ctrl_data; + } + if (wsa_priv->child_count < WSA_MACRO_CHILD_DEVICES_MAX) + wsa_priv->pdev_child_devices[ + wsa_priv->child_count++] = pdev; + else + goto err; + } + + return; +fail_pdev_add: + for (count = 0; count < wsa_priv->child_count; count++) + platform_device_put(wsa_priv->pdev_child_devices[count]); +err: + return; +} +#endif + +static int wsa_macro_component_probe(struct snd_soc_component *comp) +{ + struct wsa_macro_priv *wsa_priv = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa_priv->regmap); + +#if 0 + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_MIX1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_VI Capture"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_AIF_ECHO Capture"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_WSA"); + snd_soc_dapm_ignore_suspend(dapm, "WSA SRC0_INP"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_TX DEC0_INP"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_TX DEC1_INP"); + snd_soc_dapm_sync(dapm); +#endif + + wsa_priv->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_0_DB; + wsa_macro_init_reg(comp); + + wsa_macro_set_spkr_mode(comp, 0); + + return 0; +} + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wsa_swrm_clock(to_wsa_macro(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + return wsa_swrm_clock(to_wsa_macro(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wsa_macro_priv *wsa = to_wsa_macro(hw); + int ret, val; + + regmap_read(wsa->regmap, BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & BIT(0); + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, + +}; + +static struct clk *wsa_macro_register_mclk_output(struct wsa_macro_priv *wsa) +{ + struct clk *parent = wsa->npl_clk; + struct device *dev = wsa->dev; + struct device_node *np = dev->of_node; + const char *parent_clk_name = NULL; + const char *clk_name = "mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int rate; + int ret; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return NULL; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wsa->hw.init = &init; + hw = &wsa->hw; + ret = clk_hw_register(wsa->dev, hw); + if (ret) + return ERR_PTR(ret); + + of_clk_add_provider(np, of_clk_src_simple_get, hw->clk); + + return NULL; +} + +static const struct snd_soc_component_driver wsa_macro_component_drv = { + .name = "WSA881x", + .probe = wsa_macro_component_probe, + .controls = wsa_macro_snd_controls, + .num_controls = ARRAY_SIZE(wsa_macro_snd_controls), + .dapm_widgets = wsa_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets), + .dapm_routes = wsa_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa_audio_map), +}; +extern bool q6core_is_adsp_ready(void); + +static int wsa_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wsa_macro_priv *wsa_priv; + struct resource *res; + struct clk *c; + int ret; + + if (!q6core_is_adsp_ready()) + return -EPROBE_DEFER; + + struct pinctrl *pinctrl; +pr_err("DEBUG: %s: %d \n", __func__, __LINE__); + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(pinctrl)) + return -EPROBE_DEFER; + + +pr_err("DEBUG: %s: %d \n", __func__, __LINE__); + wsa_priv = devm_kzalloc(dev, sizeof(struct wsa_macro_priv), + GFP_KERNEL); + if (!wsa_priv) + return -ENOMEM; + + wsa_priv->hw_vote = devm_clk_get(dev, "macro"); + if (IS_ERR(wsa_priv->hw_vote)) + return PTR_ERR(wsa_priv->hw_vote); + + wsa_priv->dcodec_vote = devm_clk_get(dev, "dcodec"); + if (IS_ERR(wsa_priv->dcodec_vote)) + return PTR_ERR(wsa_priv->dcodec_vote); + + clk_prepare_enable(wsa_priv->hw_vote); + clk_prepare_enable(wsa_priv->dcodec_vote); + + wsa_priv->clk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wsa_priv->clk)) + return PTR_ERR(wsa_priv->clk); + + wsa_priv->npl_clk = devm_clk_get(dev, "npl"); + if (IS_ERR(wsa_priv->npl_clk)) + return PTR_ERR(wsa_priv->npl_clk); + + clk_set_rate(wsa_priv->clk, 19200000); + + clk_set_rate(wsa_priv->npl_clk, 19200000); + + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wsa_priv->regmap = devm_regmap_init_mmio(dev, + devm_ioremap_resource(dev, res), + &bolero_regmap_config); + + dev_set_drvdata(dev, wsa_priv); + mutex_init(&wsa_priv->mclk_lock); + mutex_init(&wsa_priv->swr_clk_lock); + + wsa_priv->bcl_pmic_params.id = 0x00; + wsa_priv->bcl_pmic_params.sid = 0x04; + wsa_priv->bcl_pmic_params.ppid = 0x3E; + + wsa_priv->reset_swr = true; + wsa_priv->dev = dev; + + unsigned int val; + regmap_read(wsa_priv->regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + pr_err("DEBUG: %s: %d %x \n", __func__, __LINE__, val); + clk_prepare_enable(wsa_priv->clk); + clk_prepare_enable(wsa_priv->npl_clk); + usleep_range(1000, 1100); + usleep_range(1000, 1100); + usleep_range(1000, 1100); + + regmap_read(wsa_priv->regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + + pr_err("DEBUG: %s: %d %x \n", __func__, __LINE__, val); + + wsa_macro_register_mclk_output(wsa_priv); + + ret = devm_snd_soc_register_component(dev, + &wsa_macro_component_drv, + wsa_macro_dai, + ARRAY_SIZE(wsa_macro_dai)); + if (ret) + return ret; + + regmap_read(wsa_priv->regmap, + BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + pr_err("DEBUG: %s: %d %x \n", __func__, __LINE__, val); + + return of_platform_populate(dev->of_node, NULL, NULL, dev); +} + +static int wsa_macro_remove(struct platform_device *pdev) +{ + struct wsa_macro_priv *wsa_priv; + //u16 count = 0; + + wsa_priv = dev_get_drvdata(&pdev->dev); + + if (!wsa_priv) + return -EINVAL; + + //for (count = 0; count < wsa_priv->child_count && + // count < WSA_MACRO_CHILD_DEVICES_MAX; count++) + // platform_device_unregister(wsa_priv->pdev_child_devices[count]); + + //pm_runtime_disable(&pdev->dev); + //pm_runtime_set_suspended(&pdev->dev); + //bolero_unregister_macro(&pdev->dev, WSA_MACRO); + mutex_destroy(&wsa_priv->mclk_lock); + mutex_destroy(&wsa_priv->swr_clk_lock); + return 0; +} + +static const struct of_device_id wsa_macro_dt_match[] = { + {.compatible = "qcom,sm8250-lpass-wsa-macro"}, + {} +}; + +#if 0 +static const struct dev_pm_ops bolero_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + pm_runtime_force_suspend, + pm_runtime_force_resume + ) + SET_RUNTIME_PM_OPS( + bolero_runtime_suspend, + bolero_runtime_resume, + NULL + ) +}; +#endif + +static struct platform_driver wsa_macro_driver = { + .driver = { + .name = "wsa_macro", + .owner = THIS_MODULE, + //.pm = &bolero_dev_pm_ops, + .of_match_table = wsa_macro_dt_match, + //.suppress_bind_attrs = true, + }, + .probe = wsa_macro_probe, + .remove = wsa_macro_remove, +}; + +module_platform_driver(wsa_macro_driver); + +MODULE_DESCRIPTION("WSA macro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h new file mode 100644 index 000000000000..5c4531c9d57d --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.h @@ -0,0 +1,622 @@ +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WSA_MACRO_SPKR_MODE_DEFAULT, + WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* Rx path gain offsets */ +enum { + WSA_MACRO_GAIN_OFFSET_M1P5_DB, + WSA_MACRO_GAIN_OFFSET_0_DB, +}; + +/* WSA - macro#2 */ +#define WSA_START_OFFSET 0 +#define BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL \ + (WSA_START_OFFSET + 0x0000) +#define BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL \ + (WSA_START_OFFSET + 0x0004) +#define BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (WSA_START_OFFSET + 0x0008) +#define BOLERO_CDC_WSA_TOP_TOP_CFG0 (WSA_START_OFFSET + 0x0080) +#define BOLERO_CDC_WSA_TOP_TOP_CFG1 (WSA_START_OFFSET + 0x0084) +#define BOLERO_CDC_WSA_TOP_FREQ_MCLK (WSA_START_OFFSET + 0x0088) +#define BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL (WSA_START_OFFSET + 0x008C) +#define BOLERO_CDC_WSA_TOP_DEBUG_EN0 (WSA_START_OFFSET + 0x0090) +#define BOLERO_CDC_WSA_TOP_DEBUG_EN1 (WSA_START_OFFSET + 0x0094) +#define BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB (WSA_START_OFFSET + 0x0098) +#define BOLERO_CDC_WSA_TOP_RX_I2S_CTL (WSA_START_OFFSET + 0x009C) +#define BOLERO_CDC_WSA_TOP_TX_I2S_CTL (WSA_START_OFFSET + 0x00A0) +#define BOLERO_CDC_WSA_TOP_I2S_CLK (WSA_START_OFFSET + 0x00A4) +#define BOLERO_CDC_WSA_TOP_I2S_RESET (WSA_START_OFFSET + 0x00A8) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (WSA_START_OFFSET + 0x0100) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (WSA_START_OFFSET + 0x0104) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (WSA_START_OFFSET + 0x0108) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (WSA_START_OFFSET + 0x010C) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (WSA_START_OFFSET + 0x0110) +#define BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (WSA_START_OFFSET + 0x0114) +#define BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (WSA_START_OFFSET + 0x0118) +/* VBAT registers */ +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL (WSA_START_OFFSET + 0x0180) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG (WSA_START_OFFSET + 0x0184) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1 (WSA_START_OFFSET + 0x0188) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2 (WSA_START_OFFSET + 0x018C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3 (WSA_START_OFFSET + 0x0190) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1 (WSA_START_OFFSET + 0x0194) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2 (WSA_START_OFFSET + 0x0198) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3 (WSA_START_OFFSET + 0x019C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1 (WSA_START_OFFSET + 0x01A0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2 (WSA_START_OFFSET + 0x01A4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1 (WSA_START_OFFSET + 0x01A8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2 (WSA_START_OFFSET + 0x01AC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3 (WSA_START_OFFSET + 0x01B0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4 (WSA_START_OFFSET + 0x01B4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1 (WSA_START_OFFSET + 0x01B8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2 (WSA_START_OFFSET + 0x01BC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3 (WSA_START_OFFSET + 0x01C0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4 (WSA_START_OFFSET + 0x01C4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5 (WSA_START_OFFSET + 0x01C8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1 (WSA_START_OFFSET + 0x01CC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON \ + (WSA_START_OFFSET + 0x01D0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL \ + (WSA_START_OFFSET + 0x01D4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN (WSA_START_OFFSET + 0x01D8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1 \ + (WSA_START_OFFSET + 0x01DC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2 \ + (WSA_START_OFFSET + 0x01E0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3 \ + (WSA_START_OFFSET + 0x01E4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4 \ + (WSA_START_OFFSET + 0x01E8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5 \ + (WSA_START_OFFSET + 0x01EC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6 \ + (WSA_START_OFFSET + 0x01F0) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7 \ + (WSA_START_OFFSET + 0x01F4) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8 \ + (WSA_START_OFFSET + 0x01F8) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9 \ + (WSA_START_OFFSET + 0x01FC) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1 (WSA_START_OFFSET + 0x0200) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2 (WSA_START_OFFSET + 0x0204) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3 (WSA_START_OFFSET + 0x0208) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1 \ + (WSA_START_OFFSET + 0x020C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2 \ + (WSA_START_OFFSET + 0x0210) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1 \ + (WSA_START_OFFSET + 0x0214) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2 \ + (WSA_START_OFFSET + 0x0218) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3 \ + (WSA_START_OFFSET + 0x021C) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4 \ + (WSA_START_OFFSET + 0x0220) +#define BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST (WSA_START_OFFSET + 0x0224) +#define BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0244) +#define BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0248) +#define BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0264) +#define BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0268) +#define BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x0284) +#define BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x0288) +#define BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL (WSA_START_OFFSET + 0x02A4) +#define BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (WSA_START_OFFSET + 0x02A8) +#define BOLERO_CDC_WSA_INTR_CTRL_CFG (WSA_START_OFFSET + 0x0340) +#define BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT (WSA_START_OFFSET + 0x0344) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0 (WSA_START_OFFSET + 0x0360) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0 (WSA_START_OFFSET + 0x0368) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (WSA_START_OFFSET + 0x0370) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0 (WSA_START_OFFSET + 0x0380) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0 (WSA_START_OFFSET + 0x0388) +#define BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (WSA_START_OFFSET + 0x0390) +#define BOLERO_CDC_WSA_INTR_CTRL_LEVEL0 (WSA_START_OFFSET + 0x03C0) +#define BOLERO_CDC_WSA_INTR_CTRL_BYPASS0 (WSA_START_OFFSET + 0x03C8) +#define BOLERO_CDC_WSA_INTR_CTRL_SET0 (WSA_START_OFFSET + 0x03D0) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CTL (WSA_START_OFFSET + 0x0400) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG0 (WSA_START_OFFSET + 0x0404) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG1 (WSA_START_OFFSET + 0x0408) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG2 (WSA_START_OFFSET + 0x040C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_CFG3 (WSA_START_OFFSET + 0x0410) +#define BOLERO_CDC_WSA_RX0_RX_VOL_CTL (WSA_START_OFFSET + 0x0414) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL (WSA_START_OFFSET + 0x0418) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG (WSA_START_OFFSET + 0x041C) +#define BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL (WSA_START_OFFSET + 0x0420) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC0 (WSA_START_OFFSET + 0x0424) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC1 (WSA_START_OFFSET + 0x0428) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC2 (WSA_START_OFFSET + 0x042C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC3 (WSA_START_OFFSET + 0x0430) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC5 (WSA_START_OFFSET + 0x0438) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC6 (WSA_START_OFFSET + 0x043C) +#define BOLERO_CDC_WSA_RX0_RX_PATH_SEC7 (WSA_START_OFFSET + 0x0440) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0 (WSA_START_OFFSET + 0x0444) +#define BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1 (WSA_START_OFFSET + 0x0448) +#define BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (WSA_START_OFFSET + 0x044C) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CTL (WSA_START_OFFSET + 0x0480) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG0 (WSA_START_OFFSET + 0x0484) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG1 (WSA_START_OFFSET + 0x0488) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG2 (WSA_START_OFFSET + 0x048C) +#define BOLERO_CDC_WSA_RX1_RX_PATH_CFG3 (WSA_START_OFFSET + 0x0490) +#define BOLERO_CDC_WSA_RX1_RX_VOL_CTL (WSA_START_OFFSET + 0x0494) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL (WSA_START_OFFSET + 0x0498) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG (WSA_START_OFFSET + 0x049C) +#define BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL (WSA_START_OFFSET + 0x04A0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC0 (WSA_START_OFFSET + 0x04A4) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC1 (WSA_START_OFFSET + 0x04A8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC2 (WSA_START_OFFSET + 0x04AC) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC3 (WSA_START_OFFSET + 0x04B0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC5 (WSA_START_OFFSET + 0x04B8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC6 (WSA_START_OFFSET + 0x04BC) +#define BOLERO_CDC_WSA_RX1_RX_PATH_SEC7 (WSA_START_OFFSET + 0x04C0) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0 (WSA_START_OFFSET + 0x04C4) +#define BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1 (WSA_START_OFFSET + 0x04C8) +#define BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (WSA_START_OFFSET + 0x04CC) +#define BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL (WSA_START_OFFSET + 0x0500) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CTL (WSA_START_OFFSET + 0x0504) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CFG1 (WSA_START_OFFSET + 0x0508) +#define BOLERO_CDC_WSA_BOOST0_BOOST_CFG2 (WSA_START_OFFSET + 0x050C) +#define BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL (WSA_START_OFFSET + 0x0540) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CTL (WSA_START_OFFSET + 0x0544) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CFG1 (WSA_START_OFFSET + 0x0548) +#define BOLERO_CDC_WSA_BOOST1_BOOST_CFG2 (WSA_START_OFFSET + 0x054C) +#define BOLERO_CDC_WSA_COMPANDER0_CTL0 (WSA_START_OFFSET + 0x0580) +#define BOLERO_CDC_WSA_COMPANDER0_CTL1 (WSA_START_OFFSET + 0x0584) +#define BOLERO_CDC_WSA_COMPANDER0_CTL2 (WSA_START_OFFSET + 0x0588) +#define BOLERO_CDC_WSA_COMPANDER0_CTL3 (WSA_START_OFFSET + 0x058C) +#define BOLERO_CDC_WSA_COMPANDER0_CTL4 (WSA_START_OFFSET + 0x0590) +#define BOLERO_CDC_WSA_COMPANDER0_CTL5 (WSA_START_OFFSET + 0x0594) +#define BOLERO_CDC_WSA_COMPANDER0_CTL6 (WSA_START_OFFSET + 0x0598) +#define BOLERO_CDC_WSA_COMPANDER0_CTL7 (WSA_START_OFFSET + 0x059C) +#define BOLERO_CDC_WSA_COMPANDER1_CTL0 (WSA_START_OFFSET + 0x05C0) +#define BOLERO_CDC_WSA_COMPANDER1_CTL1 (WSA_START_OFFSET + 0x05C4) +#define BOLERO_CDC_WSA_COMPANDER1_CTL2 (WSA_START_OFFSET + 0x05C8) +#define BOLERO_CDC_WSA_COMPANDER1_CTL3 (WSA_START_OFFSET + 0x05CC) +#define BOLERO_CDC_WSA_COMPANDER1_CTL4 (WSA_START_OFFSET + 0x05D0) +#define BOLERO_CDC_WSA_COMPANDER1_CTL5 (WSA_START_OFFSET + 0x05D4) +#define BOLERO_CDC_WSA_COMPANDER1_CTL6 (WSA_START_OFFSET + 0x05D8) +#define BOLERO_CDC_WSA_COMPANDER1_CTL7 (WSA_START_OFFSET + 0x05DC) +#define BOLERO_CDC_WSA_SOFTCLIP0_CRC (WSA_START_OFFSET + 0x0600) +#define BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (WSA_START_OFFSET + 0x0604) +#define BOLERO_CDC_WSA_SOFTCLIP1_CRC (WSA_START_OFFSET + 0x0640) +#define BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (WSA_START_OFFSET + 0x0644) +#define BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL \ + (WSA_START_OFFSET + 0x0680) +#define BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (WSA_START_OFFSET + 0x0684) +#define BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL \ + (WSA_START_OFFSET + 0x06C0) +#define BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (WSA_START_OFFSET + 0x06C4) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (WSA_START_OFFSET + 0x0700) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0 (WSA_START_OFFSET + 0x0704) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1 (WSA_START_OFFSET + 0x0708) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL (WSA_START_OFFSET + 0x070C) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB \ + (WSA_START_OFFSET + 0x0710) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB \ + (WSA_START_OFFSET + 0x0714) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB \ + (WSA_START_OFFSET + 0x0718) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB \ + (WSA_START_OFFSET + 0x071C) +#define BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (WSA_START_OFFSET + 0x0720) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (WSA_START_OFFSET + 0x0740) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0 (WSA_START_OFFSET + 0x0744) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1 (WSA_START_OFFSET + 0x0748) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL (WSA_START_OFFSET + 0x074C) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB \ + (WSA_START_OFFSET + 0x0750) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB \ + (WSA_START_OFFSET + 0x0754) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB \ + (WSA_START_OFFSET + 0x0758) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB \ + (WSA_START_OFFSET + 0x075C) +#define BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (WSA_START_OFFSET + 0x0760) +#define WSA_MAX_OFFSET (WSA_START_OFFSET + 0x0760) + +#define BOLERO_CDC_WSA_MACRO_MAX 0x1D9 /* 0x760/4 = 0x1D8 + 1 registers */ + +static const struct reg_default bolero_defaults[] = { + /* WSA Macro */ + { BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { BOLERO_CDC_WSA_TOP_TOP_CFG0, 0x00}, + { BOLERO_CDC_WSA_TOP_TOP_CFG1, 0x00}, + { BOLERO_CDC_WSA_TOP_FREQ_MCLK, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_EN0, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_EN1, 0x00}, + { BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB, 0x88}, + { BOLERO_CDC_WSA_TOP_RX_I2S_CTL, 0x0C}, + { BOLERO_CDC_WSA_TOP_TX_I2S_CTL, 0x0C}, + { BOLERO_CDC_WSA_TOP_I2S_CLK, 0x02}, + { BOLERO_CDC_WSA_TOP_I2S_RESET, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG, 0x10}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3, 0x04}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1, 0xE0}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3, 0x40}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1, 0x2A}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2, 0x18}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3, 0x18}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4, 0x03}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4, 0x64}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN, 0x0C}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2, 0x77}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5, 0x4B}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7, 0x01}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1, 0x04}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2, 0x08}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3, 0x0C}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1, 0xE0}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4, 0x00}, + { BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST, 0x00}, + { BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_CFG, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_LEVEL0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_BYPASS0, 0x00}, + { BOLERO_CDC_WSA_INTR_CTRL_SET0, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CTL, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_WSA_RX0_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC0, 0x04}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC5, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC6, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG0, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG1, 0x64}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG2, 0x8F}, + { BOLERO_CDC_WSA_RX1_RX_PATH_CFG3, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_VOL_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E}, + { BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC0, 0x04}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC1, 0x08}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC2, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC3, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC5, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC6, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_SEC7, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08}, + { BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00}, + { BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CTL, 0xD0}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CFG1, 0x89}, + { BOLERO_CDC_WSA_BOOST0_BOOST_CFG2, 0x04}, + { BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CTL, 0xD0}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CFG1, 0x89}, + { BOLERO_CDC_WSA_BOOST1_BOOST_CFG2, 0x04}, + { BOLERO_CDC_WSA_COMPANDER0_CTL0, 0x60}, + { BOLERO_CDC_WSA_COMPANDER0_CTL1, 0xDB}, + { BOLERO_CDC_WSA_COMPANDER0_CTL2, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER0_CTL3, 0x35}, + { BOLERO_CDC_WSA_COMPANDER0_CTL4, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER0_CTL5, 0x00}, + { BOLERO_CDC_WSA_COMPANDER0_CTL6, 0x01}, + { BOLERO_CDC_WSA_COMPANDER0_CTL7, 0x28}, + { BOLERO_CDC_WSA_COMPANDER1_CTL0, 0x60}, + { BOLERO_CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { BOLERO_CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER1_CTL3, 0x35}, + { BOLERO_CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { BOLERO_CDC_WSA_COMPANDER1_CTL5, 0x00}, + { BOLERO_CDC_WSA_COMPANDER1_CTL6, 0x01}, + { BOLERO_CDC_WSA_COMPANDER1_CTL7, 0x28}, + { BOLERO_CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { BOLERO_CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, + { BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + { BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + { BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, +}; + +#define BOLERO_REG(x) ((x) / 4) +#define RD_REG 1 +#define WR_REG 2 +#define RD_WR_REG (RD_REG | WR_REG) + +u8 bolero_wsa_reg_access[BOLERO_CDC_WSA_MACRO_MAX] = { + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TOP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TOP_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_FREQ_MCLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_BUS_SEL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_EN0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_EN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_DEBUG_DSM_LB)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_RX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_TX_I2S_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_I2S_CLK)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TOP_I2S_RESET)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT0_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_INT1_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_MIX_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_RX_EC_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_ADC_CAL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_PK_EST3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_RF_PROC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_TAC4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DEBUG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_UPD_MON)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BAN)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD8)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_GAIN_UPD9)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_BCL_ATTN3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_CFG4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX0_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX1_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX2_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_TX3_SPKR_PROT_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_CLR_COMMIT)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN1_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_MASK0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_PIN2_CLEAR0)] = WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_LEVEL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_BYPASS0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_INTR_CTRL_SET0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX0_RX_PATH_DSMDEM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_CFG3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_VOL_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_CFG)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_VOL_MIX_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC6)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_SEC7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_MIX_SEC1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_RX1_RX_PATH_DSMDEM_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST0_BOOST_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CFG1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_BOOST1_BOOST_CFG2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER0_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL2)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL3)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL4)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL5)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL6)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_COMPANDER1_CTL7)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP0_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP1_CRC)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CTL0)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_CTL1)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_FIFO_CTL)] = RD_WR_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB)] = RD_REG, + [BOLERO_REG(BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO)] = RD_REG, +}; + +static bool bolero_is_readable_register(struct device *dev, + unsigned int reg) +{ + return bolero_wsa_reg_access[BOLERO_REG(reg)] & RD_REG; +} + +static bool bolero_is_writeable_register(struct device *dev, + unsigned int reg) +{ + return bolero_wsa_reg_access[BOLERO_REG(reg)] & WR_REG; +} + +static bool bolero_is_volatile_register(struct device *dev, + unsigned int reg) +{ + /* Update volatile list for rx/tx macros */ + switch (reg) { + case BOLERO_CDC_WSA_VBAT_BCL_VBAT_GAIN_MON_VAL: + case BOLERO_CDC_WSA_VBAT_BCL_VBAT_DECODE_ST: + case BOLERO_CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case BOLERO_CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case BOLERO_CDC_WSA_COMPANDER0_CTL6: + case BOLERO_CDC_WSA_COMPANDER1_CTL6: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + return true; + } + return false; +} + +const struct regmap_config bolero_regmap_config = { + .reg_bits = 16, + .val_bits = 32, /* 8 but with 32 bit read/write */ + .reg_stride = 4, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = bolero_defaults, + .num_reg_defaults = ARRAY_SIZE(bolero_defaults), + .max_register = WSA_MAX_OFFSET, + .writeable_reg = bolero_is_writeable_register, + .volatile_reg = bolero_is_volatile_register, + .readable_reg = bolero_is_readable_register, +}; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 5d6b2466a2f2..fbd118e86631 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,11 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_QCOM +menuconfig SND_SOC_QCOM tristate "ASoC support for QCOM platforms" depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices in Qualcomm Technologies SOC-based platforms. +if SND_SOC_QCOM + config SND_SOC_LPASS_CPU tristate select REGMAP_MMIO @@ -26,7 +28,6 @@ config SND_SOC_LPASS_APQ8016 config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -35,7 +36,6 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 select SND_SOC_QCOM_COMMON help @@ -58,6 +58,9 @@ config SND_SOC_QDSP6_AFE config SND_SOC_QDSP6_AFE_DAI tristate +config SND_SOC_QDSP6_AFE_CLOCKS + tristate + config SND_SOC_QDSP6_ADM tristate @@ -78,6 +81,7 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_AFE_DAI + select SND_SOC_QDSP6_AFE_CLOCKS select SND_SOC_QDSP6_ADM select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_ASM @@ -110,3 +114,15 @@ config SND_SOC_SDM845 To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs. + +config SND_SOC_SM8250 + tristate "SoC Machine driver for SM8250 boards" + depends on QCOM_APR + select SND_SOC_QDSP6 + select SND_SOC_QCOM_COMMON + help + To add support for audio on Qualcomm Technologies Inc. + SM8250 SoC-based systems. + Say Y if you want to use audio device on this SoCs. + +endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 41b2c7a23a4d..8236eed36c22 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -15,12 +15,14 @@ snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o snd-soc-apq8096-objs := apq8096.o snd-soc-sdm845-objs := sdm845.o +snd-soc-sm8250-objs := sm8250.o snd-soc-qcom-common-objs := common.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o +obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o #DSP lib diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 253549600c5a..1a55251d90e7 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -5,14 +5,21 @@ #include <linux/platform_device.h> #include <linux/of_device.h> #include <sound/soc.h> +#include <sound/jack.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> +#include <uapi/linux/input-event-codes.h> #include "common.h" #define SLIM_MAX_TX_PORTS 16 #define SLIM_MAX_RX_PORTS 16 #define WCD9335_DEFAULT_MCLK_RATE 9600000 +struct apq8096_card_data { + struct snd_soc_jack jack; + bool jack_setup; +}; + static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -67,6 +74,7 @@ static struct snd_soc_ops apq8096_ops = { static int apq8096_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct apq8096_card_data *data = snd_soc_card_get_drvdata(rtd->card); /* * Codec SLIMBUS configuration @@ -79,6 +87,8 @@ static int apq8096_init(struct snd_soc_pcm_runtime *rtd) unsigned int tx_ch[SLIM_MAX_TX_PORTS] = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + struct snd_soc_card *card = rtd->card; + int rval; snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), tx_ch, ARRAY_SIZE(rx_ch), rx_ch); @@ -86,6 +96,38 @@ static int apq8096_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dai_set_sysclk(codec_dai, 0, WCD9335_DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + if (!data->jack_setup) { + struct snd_jack *jack; + + rval = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4, + &data->jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add Headphone Jack\n"); + return rval; + } + + jack = data->jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + data->jack_setup = true; + } + + rval = snd_soc_component_set_jack(codec_dai->component, + &data->jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + return 0; } @@ -105,6 +147,7 @@ static void apq8096_add_be_ops(struct snd_soc_card *card) static int apq8096_platform_probe(struct platform_device *pdev) { + struct apq8096_card_data *data; struct snd_soc_card *card; struct device *dev = &pdev->dev; int ret; @@ -113,8 +156,13 @@ static int apq8096_platform_probe(struct platform_device *pdev) if (!card) return -ENOMEM; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + card->dev = dev; dev_set_drvdata(dev, card); + snd_soc_card_set_drvdata(card, data); ret = qcom_snd_parse_of(card); if (ret) return ret; diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 7e91e96f7ad5..3c1dd9f32f1d 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE_CLOCKS) += q6afe-clocks.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c new file mode 100644 index 000000000000..6d4902d5d155 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-1.0 +// Copyright (c) 2020, Linaro Limited + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/slab.h> +#include "q6afe.h" + +#define Q6AFE_CORE_CLK(id) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = Q6AFE_##id, \ + .name = #id, \ + .rate = 19200000, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_q6afe_ops, \ + .name = #id, \ + }, \ + } + +#define Q6AFE_NPL_CLK(cid, id) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = Q6AFE_##id, \ + .name = #id, \ + .rate = 19200000, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_q6afe_ops, \ + .name = #id, \ + }, \ + } + +#define Q6AFE_CLK(id) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = Q6AFE_##id, \ + .name = #id, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_q6afe_ops, \ + .name = #id, \ + }, \ + } + +#define Q6AFE_VOTE_CLK(id, blkid, n) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = blkid, \ + .name = #n, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_vote_q6afe_ops, \ + .name = #id, \ + }, \ + } + +struct q6afe_clk { + struct device *dev; + int clk_id; + int afe_clk_id; + char *name; + int attributes; + int rate; + uint32_t handle; + struct clk_hw hw; +}; + +#define to_q6afe_clk(_hw) container_of(_hw, struct q6afe_clk, hw) + +struct q6afe_cc { + struct device *dev; + struct q6afe_clk **clks; + int num_clks; +}; + +static int clk_q6afe_enable(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, + LPASS_CLK_ATTRIBUTE_COUPLE_NO/*clk->attributes*/, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, clk->rate); +} + +static void clk_q6afe_disable(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, + LPASS_CLK_ATTRIBUTE_COUPLE_NO/*clk->attributes*/, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0); +} + +static int clk_q6afe_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + clk->rate = rate; + + return 0; +} + +static unsigned long clk_q6afe_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return clk->rate; +} + +static long clk_q6afe_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static const struct clk_ops clk_q6afe_ops = { + .enable = clk_q6afe_enable, + .disable = clk_q6afe_disable, + .set_rate = clk_q6afe_set_rate, + .round_rate = clk_q6afe_round_rate, + .recalc_rate = clk_q6afe_recalc_rate, +}; + +static int clk_vote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_vote_lpass_core_hw(clk->dev, clk->afe_clk_id, + clk->name, &clk->handle); +} + +static void clk_unvote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_unvote_lpass_core_hw(clk->dev, clk->afe_clk_id, clk->handle); +} + +static const struct clk_ops clk_vote_q6afe_ops = { + .enable = clk_vote_q6afe_block, + .disable = clk_unvote_q6afe_block, +}; + +struct q6afe_clk *q6afe_clks[Q6AFE_MAX_CLK_ID] = { + [LPASS_CLK_ID_PRI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT), + [LPASS_CLK_ID_PRI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT), + [LPASS_CLK_ID_SEC_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT), + [LPASS_CLK_ID_SEC_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT), + [LPASS_CLK_ID_TER_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_IBIT), + [LPASS_CLK_ID_TER_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_EBIT), + [LPASS_CLK_ID_QUAD_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT), + [LPASS_CLK_ID_QUAD_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT), + [LPASS_CLK_ID_SPEAKER_I2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_OSR] = Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR), + [LPASS_CLK_ID_QUI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT), + [LPASS_CLK_ID_SEN_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT), + [LPASS_CLK_ID_SEN_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT), + [LPASS_CLK_ID_INT0_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT), + [LPASS_CLK_ID_INT1_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT), + [LPASS_CLK_ID_INT2_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT), + [LPASS_CLK_ID_INT3_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT), + [LPASS_CLK_ID_INT4_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT), + [LPASS_CLK_ID_INT5_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), + [LPASS_CLK_ID_INT6_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + [LPASS_CLK_ID_PRI_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_IBIT), + [LPASS_CLK_ID_PRI_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_EBIT), + [LPASS_CLK_ID_SEC_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_IBIT), + [LPASS_CLK_ID_SEC_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_EBIT), + [LPASS_CLK_ID_TER_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_IBIT), + [LPASS_CLK_ID_TER_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_EBIT), + [LPASS_CLK_ID_QUAD_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_IBIT), + [LPASS_CLK_ID_QUAD_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_EBIT), + [LPASS_CLK_ID_QUIN_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_IBIT), + [LPASS_CLK_ID_QUIN_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_EBIT), + [LPASS_CLK_ID_QUI_PCM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_PCM_OSR), + [LPASS_CLK_ID_PRI_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_IBIT), + [LPASS_CLK_ID_PRI_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_EBIT), + [LPASS_CLK_ID_SEC_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_IBIT), + [LPASS_CLK_ID_SEC_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_EBIT), + [LPASS_CLK_ID_TER_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_IBIT), + [LPASS_CLK_ID_TER_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_EBIT), + [LPASS_CLK_ID_QUAD_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_IBIT), + [LPASS_CLK_ID_QUAD_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_IBIT), + [LPASS_CLK_ID_QUIN_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_OSR), + [LPASS_CLK_ID_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_1), + [LPASS_CLK_ID_MCLK_2] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_2), + [LPASS_CLK_ID_MCLK_3] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_3), + [LPASS_CLK_ID_MCLK_4] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_4), + [LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE] = Q6AFE_CLK(LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE), + [LPASS_CLK_ID_INT_MCLK_0] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_0), + [LPASS_CLK_ID_INT_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_1), + [LPASS_CLK_ID_WSA_CORE_MCLK] = Q6AFE_CORE_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), + [LPASS_CLK_ID_WSA_CORE_NPL_MCLK] = + Q6AFE_NPL_CLK(LPASS_CLK_ID_WSA_CORE_MCLK, + LPASS_CLK_ID_WSA_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_MCLK] = Q6AFE_CORE_CLK(LPASS_CLK_ID_VA_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_MCLK] = Q6AFE_CORE_CLK(LPASS_CLK_ID_TX_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_NPL_MCLK] = + Q6AFE_NPL_CLK(LPASS_CLK_ID_TX_CORE_MCLK, + LPASS_CLK_ID_TX_CORE_NPL_MCLK), + [LPASS_CLK_ID_RX_CORE_MCLK] = Q6AFE_CORE_CLK(LPASS_CLK_ID_RX_CORE_MCLK), + [LPASS_CLK_ID_RX_CORE_NPL_MCLK] = + Q6AFE_NPL_CLK(LPASS_CLK_ID_RX_CORE_MCLK, + LPASS_CLK_ID_RX_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_2X_MCLK] = + Q6AFE_NPL_CLK(LPASS_CLK_ID_VA_CORE_MCLK, + LPASS_CLK_ID_VA_CORE_2X_MCLK), + [LPASS_HW_AVTIMER_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_AVTIMER_VOTE, + Q6AFE_LPASS_CORE_AVTIMER_BLOCK, + "LPASS_AVTIMER_MACRO" + ), + [LPASS_HW_MACRO_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_MACRO_VOTE, + Q6AFE_LPASS_CORE_HW_MACRO_BLOCK, + "LPASS_HW_MACRO"), + [LPASS_HW_DCODEC_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_DCODEC_VOTE, + Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK, + "LPASS_HW_DCODEC"), +}; + +static struct clk_hw *q6afe_of_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct q6afe_cc *cc = data; + unsigned int idx = clkspec->args[0]; + unsigned int attr = clkspec->args[1]; + + if (idx >= cc->num_clks || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) { + dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr); + return ERR_PTR(-EINVAL); + } + + if (cc->clks[idx]) { + cc->clks[idx]->attributes = attr; + return &cc->clks[idx]->hw; + } + + return ERR_PTR(-ENOENT); +} + +static int q6afe_clock_dev_probe(struct platform_device *pdev) +{ + struct q6afe_cc *cc; + struct device *dev = &pdev->dev; + int i, ret; + + cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); + if (!cc) + return -ENOMEM; + + cc->clks = &q6afe_clks[0]; + + cc->num_clks = ARRAY_SIZE(q6afe_clks); + for (i = 0; i < ARRAY_SIZE(q6afe_clks); i++) { + if (!q6afe_clks[i]) + continue; + + q6afe_clks[i]->dev = dev; + + ret = devm_clk_hw_register(dev, &q6afe_clks[i]->hw); + if (ret) + return ret; + } + + ret = of_clk_add_hw_provider(dev->of_node, q6afe_of_clk_hw_get, cc); + if (ret) + return ret; + + dev_set_drvdata(dev, cc); + + return 0; +} + +static const struct of_device_id q6afe_clock_device_id[] = { + { .compatible = "qcom,q6afe-clocks" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6afe_clock_device_id); + +static struct platform_driver q6afe_clock_platform_driver = { + .driver = { + .name = "q6afe-clock", + .of_match_table = of_match_ptr(q6afe_clock_device_id), + }, + .probe = q6afe_clock_dev_probe, +}; +module_platform_driver(q6afe_clock_platform_driver); + +MODULE_DESCRIPTION("Q6 Audio Frontend clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 0168af849272..d58b86a98114 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -55,6 +55,48 @@ .remove = msm_dai_q6_dai_remove, \ } +#define Q6AFE_CDC_DMA_RX_DAI(did) { \ + .playback = { \ + .stream_name = #did" Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + +#define Q6AFE_CDC_DMA_TX_DAI(did) { \ + .capture = { \ + .stream_name = #did" Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + struct q6afe_dai_priv_data { uint32_t sd_line_mask; uint32_t sync_mode; @@ -307,6 +349,90 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream, return 0; } + +static int q6dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_ch_mask, + unsigned int rx_num, unsigned int *rx_ch_mask) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + int ch_mask; + int rc = 0; + + switch (dai->id) { + case WSA_CODEC_DMA_TX_0: + case WSA_CODEC_DMA_TX_1: + case WSA_CODEC_DMA_TX_2: + case VA_CODEC_DMA_TX_0: + case VA_CODEC_DMA_TX_1: + case VA_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: + case TX_CODEC_DMA_TX_4: + case TX_CODEC_DMA_TX_5: + if (!tx_ch_mask) { + dev_err(dai->dev, "tx slot not found\n"); + return -EINVAL; + } + + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid tx num %d\n", + tx_num); + return -EINVAL; + } + ch_mask = *tx_ch_mask; + + break; + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_2: + case RX_CODEC_DMA_RX_3: + case RX_CODEC_DMA_RX_4: + case RX_CODEC_DMA_RX_5: + case RX_CODEC_DMA_RX_6: + case RX_CODEC_DMA_RX_7: + /* rx */ + if (!rx_ch_mask) { + dev_err(dai->dev, "rx slot not found\n"); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid rx num %d\n", + rx_num); + return -EINVAL; + } + ch_mask = *rx_ch_mask; + + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + cfg->active_channels_mask = ch_mask; + + return rc; +} + +static int q6dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + + cfg->bit_width = params_width(params); + cfg->sample_rate = params_rate(params); + cfg->num_channels = params_channels(params); + + return 0; +} static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -362,6 +488,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, q6afe_tdm_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].tdm); break; + case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7: + q6afe_cdc_dma_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].dma_cfg); + break; default: return -EINVAL; } @@ -430,6 +560,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, freq, dir); case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: return q6afe_port_set_sysclk(port, clk_id, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, @@ -562,6 +693,29 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"PRI_MI2S_TX", NULL, "Primary MI2S Capture"}, {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, + + {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"}, + {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"}, + {"WSA_CODEC_DMA_RX_1 Playback", NULL, "WSA_CODEC_DMA_RX_1"}, + {"WSA_CODEC_DMA_TX_1", NULL, "WSA_CODEC_DMA_TX_1 Capture"}, + {"WSA_CODEC_DMA_TX_2", NULL, "WSA_CODEC_DMA_TX_2 Capture"}, + {"VA_CODEC_DMA_TX_0", NULL, "VA_CODEC_DMA_TX_0 Capture"}, + {"VA_CODEC_DMA_TX_1", NULL, "VA_CODEC_DMA_TX_1 Capture"}, + {"VA_CODEC_DMA_TX_2", NULL, "VA_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_0 Playback", NULL, "RX_CODEC_DMA_RX_0"}, + {"TX_CODEC_DMA_TX_0", NULL, "TX_CODEC_DMA_TX_0 Capture"}, + {"RX_CODEC_DMA_RX_1 Playback", NULL, "RX_CODEC_DMA_RX_1"}, + {"TX_CODEC_DMA_TX_1", NULL, "TX_CODEC_DMA_TX_1 Capture"}, + {"RX_CODEC_DMA_RX_2 Playback", NULL, "RX_CODEC_DMA_RX_2"}, + {"TX_CODEC_DMA_TX_2", NULL, "TX_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_3 Playback", NULL, "RX_CODEC_DMA_RX_3"}, + {"TX_CODEC_DMA_TX_3", NULL, "TX_CODEC_DMA_TX_3 Capture"}, + {"RX_CODEC_DMA_RX_4 Playback", NULL, "RX_CODEC_DMA_RX_4"}, + {"TX_CODEC_DMA_TX_4", NULL, "TX_CODEC_DMA_TX_4 Capture"}, + {"RX_CODEC_DMA_RX_5 Playback", NULL, "RX_CODEC_DMA_RX_5"}, + {"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"}, + {"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"}, + {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"}, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -594,6 +748,14 @@ static const struct snd_soc_dai_ops q6tdm_ops = { .hw_params = q6tdm_hw_params, }; +static const struct snd_soc_dai_ops q6dma_ops = { + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, +}; + static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -1128,6 +1290,28 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, }, + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7), }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, @@ -1350,6 +1534,51 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_6", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL", + 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index e0945f7a58c8..da0f7cebd245 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -42,6 +42,9 @@ #define AFE_PARAM_ID_I2S_CONFIG 0x0001020D #define AFE_PARAM_ID_TDM_CONFIG 0x0001029D #define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 +#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6 /* I2S config specific */ #define AFE_API_VERSION_I2S_CONFIG 0x1 @@ -299,22 +302,71 @@ #define AFE_PORT_ID_QUINARY_TDM_TX_7 \ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) +/* AFE WSA Codec DMA Rx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_0 0xB000 +/* AFE WSA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_0 0xB001 +/* AFE WSA Codec DMA Rx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_1 0xB002 +/* AFE WSA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_1 0xB003 +/* AFE WSA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_2 0xB005 +/* AFE VA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_0 0xB021 +/* AFE VA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_1 0xB023 +/* AFE VA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_2 0xB025 +/* AFE Rx Codec DMA Rx port 0 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_0 0xB030 +/* AFE Tx Codec DMA Tx port 0 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_0 0xB031 +/* AFE Rx Codec DMA Rx port 1 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_1 0xB032 +/* AFE Tx Codec DMA Tx port 1 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_1 0xB033 +/* AFE Rx Codec DMA Rx port 2 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_2 0xB034 +/* AFE Tx Codec DMA Tx port 2 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_2 0xB035 +/* AFE Rx Codec DMA Rx port 3 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_3 0xB036 +/* AFE Tx Codec DMA Tx port 3 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_3 0xB037 +/* AFE Rx Codec DMA Rx port 4 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_4 0xB038 +/* AFE Tx Codec DMA Tx port 4 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_4 0xB039 +/* AFE Rx Codec DMA Rx port 5 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_5 0xB03A +/* AFE Tx Codec DMA Tx port 5 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_5 0xB03B +/* AFE Rx Codec DMA Rx port 6 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_6 0xB03C +/* AFE Rx Codec DMA Rx port 7 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_7 0xB03E + #define Q6AFE_LPASS_MODE_CLK1_VALID 1 #define Q6AFE_LPASS_MODE_CLK2_VALID 2 #define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 #define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 #define AFE_API_VERSION_TDM_CONFIG 1 #define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1 +#define AFE_API_VERSION_CODEC_DMA_CONFIG 1 #define TIMEOUT_MS 1000 #define AFE_CMD_RESP_AVAIL 0 #define AFE_CMD_RESP_NONE 1 +#define AFE_CLK_TOKEN 1024 struct q6afe { struct apr_device *apr; struct device *dev; struct q6core_svc_api_info ainfo; struct mutex lock; + struct aprv2_ibasic_rsp_result_t result; + wait_queue_head_t wait; struct list_head port_list; spinlock_t port_list_lock; }; @@ -448,11 +500,21 @@ struct afe_param_id_tdm_cfg { u32 slot_mask; } __packed; +struct afe_param_id_cdc_dma_cfg { + u32 cdc_dma_cfg_minor_version; + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +} __packed; + union afe_port_config { struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; struct afe_param_id_slimbus_cfg slim_cfg; struct afe_param_id_i2s_cfg i2s_cfg; struct afe_param_id_tdm_cfg tdm_cfg; + struct afe_param_id_cdc_dma_cfg dma_cfg; } __packed; @@ -486,6 +548,18 @@ struct q6afe_port { struct list_head node; }; +struct afe_cmd_remote_lpass_core_hw_vote_request { + uint32_t hw_block_id; + char client_name[8]; +} __packed; + +struct afe_cmd_remote_lpass_core_hw_devote_request { + uint32_t hw_block_id; + uint32_t client_handle; +} __packed; + + + struct afe_port_map { int port_id; int token; @@ -707,6 +781,50 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_TDM_TX_7, 0, 1}, [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX, DISPLAY_PORT_RX, 1, 1}, + [WSA_CODEC_DMA_RX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + WSA_CODEC_DMA_RX_0, 1, 1}, + [WSA_CODEC_DMA_TX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + WSA_CODEC_DMA_TX_0, 0, 1}, + [WSA_CODEC_DMA_RX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + WSA_CODEC_DMA_RX_1, 1, 1}, + [WSA_CODEC_DMA_TX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + WSA_CODEC_DMA_TX_1, 0, 1}, + [WSA_CODEC_DMA_TX_2] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + WSA_CODEC_DMA_TX_2, 0, 1}, + [VA_CODEC_DMA_TX_0] = { AFE_PORT_ID_VA_CODEC_DMA_TX_0, + VA_CODEC_DMA_TX_0, 0, 1}, + [VA_CODEC_DMA_TX_1] = { AFE_PORT_ID_VA_CODEC_DMA_TX_1, + VA_CODEC_DMA_TX_1, 0, 1}, + [VA_CODEC_DMA_TX_2] = { AFE_PORT_ID_VA_CODEC_DMA_TX_2, + VA_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_0] = { AFE_PORT_ID_RX_CODEC_DMA_RX_0, + RX_CODEC_DMA_RX_0, 1, 1}, + [TX_CODEC_DMA_TX_0] = { AFE_PORT_ID_TX_CODEC_DMA_TX_0, + TX_CODEC_DMA_TX_0, 0, 1}, + [RX_CODEC_DMA_RX_1] = { AFE_PORT_ID_RX_CODEC_DMA_RX_1, + RX_CODEC_DMA_RX_1, 1, 1}, + [TX_CODEC_DMA_TX_1] = { AFE_PORT_ID_TX_CODEC_DMA_TX_1, + TX_CODEC_DMA_TX_1, 0, 1}, + [RX_CODEC_DMA_RX_2] = { AFE_PORT_ID_RX_CODEC_DMA_RX_2, + RX_CODEC_DMA_RX_2, 1, 1}, + [TX_CODEC_DMA_TX_2] = { AFE_PORT_ID_TX_CODEC_DMA_TX_2, + TX_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_3] = { AFE_PORT_ID_RX_CODEC_DMA_RX_3, + RX_CODEC_DMA_RX_3, 1, 1}, + [TX_CODEC_DMA_TX_3] = { AFE_PORT_ID_TX_CODEC_DMA_TX_3, + TX_CODEC_DMA_TX_3, 0, 1}, + [RX_CODEC_DMA_RX_4] = { AFE_PORT_ID_RX_CODEC_DMA_RX_4, + RX_CODEC_DMA_RX_4, 1, 1}, + [TX_CODEC_DMA_TX_4] = { AFE_PORT_ID_TX_CODEC_DMA_TX_4, + TX_CODEC_DMA_TX_4, 0, 1}, + [RX_CODEC_DMA_RX_5] = { AFE_PORT_ID_RX_CODEC_DMA_RX_5, + RX_CODEC_DMA_RX_5, 1, 1}, + [TX_CODEC_DMA_TX_5] = { AFE_PORT_ID_TX_CODEC_DMA_TX_5, + TX_CODEC_DMA_TX_5, 0, 1}, + [RX_CODEC_DMA_RX_6] = { AFE_PORT_ID_RX_CODEC_DMA_RX_6, + RX_CODEC_DMA_RX_6, 1, 1}, + [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, + RX_CODEC_DMA_RX_7, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -769,8 +887,16 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) port->result = *res; wake_up(&port->wait); kref_put(&port->refcount, q6afe_port_free); + } else if (hdr->token == AFE_CLK_TOKEN) { + afe->result = *res; + wake_up(&afe->wait); } break; + case AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + case AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST: + afe->result = *res; + wake_up(&afe->wait); + break; default: dev_err(afe->dev, "Unknown cmd 0x%x\n", res->opcode); break; @@ -801,15 +927,24 @@ int q6afe_get_port_id(int index) EXPORT_SYMBOL_GPL(q6afe_get_port_id); static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, - struct q6afe_port *port) + struct q6afe_port *port, bool should_wait) { wait_queue_head_t *wait = &port->wait; struct apr_hdr *hdr = &pkt->hdr; + struct aprv2_ibasic_rsp_result_t *result; int ret; mutex_lock(&afe->lock); - port->result.opcode = 0; - port->result.status = 0; + if (port) { + wait = &port->wait; + result = &port->result; + } else { + result = &afe->result; + wait = &afe->wait; + } + + result->opcode = 0; + result->status = 0; ret = apr_send_pkt(afe->apr, pkt); if (ret < 0) { @@ -818,13 +953,18 @@ static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, goto err; } - ret = wait_event_timeout(*wait, (port->result.opcode == hdr->opcode), + if (!should_wait) { + ret = 0; + goto err; + } + + ret = wait_event_timeout(*wait, (result->opcode == hdr->opcode), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { ret = -ETIMEDOUT; - } else if (port->result.status > 0) { + } else if (result->status > 0) { dev_err(afe->dev, "DSP returned error[%x]\n", - port->result.status); + result->status); ret = -EINVAL; } else { ret = 0; @@ -836,14 +976,13 @@ err: return ret; } -static int q6afe_port_set_param(struct q6afe_port *port, void *data, - int param_id, int module_id, int psize) +static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port, + void *data, int param_id, int module_id, int psize, + int token, bool wait) { struct afe_svc_cmd_set_param *param; struct afe_port_param_data_v2 *pdata; - struct q6afe *afe = port->afe; struct apr_pkt *pkt; - u16 port_id = port->id; int ret, pkt_size; void *p, *pl; @@ -864,7 +1003,7 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pkt->hdr.pkt_size = pkt_size; pkt->hdr.src_port = 0; pkt->hdr.dest_port = 0; - pkt->hdr.token = port->token; + pkt->hdr.token = token; pkt->hdr.opcode = AFE_SVC_CMD_SET_PARAM; param->payload_size = sizeof(*pdata) + psize; @@ -875,15 +1014,21 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, wait); if (ret) - dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", - port_id, ret); + dev_err(afe->dev, "AFE set params failed %d\n", ret); kfree(pkt); return ret; } +static int q6afe_port_set_param(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + return q6afe_set_param(port->afe, port, data, param_id, module_id, + psize, port->token, true); +} + static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, int param_id, int module_id, int psize) { @@ -924,7 +1069,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, true); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -933,7 +1078,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, return ret; } -static int q6afe_set_lpass_clock(struct q6afe_port *port, +static int q6afe_port_set_lpass_clock(struct q6afe_port *port, struct afe_clk_cfg *cfg) { return q6afe_port_set_param_v2(port, cfg, @@ -958,6 +1103,28 @@ static int q6afe_set_digital_codec_core_clock(struct q6afe_port *port, sizeof(*cfg)); } +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int attri, + int clk_root, unsigned int freq) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_clk_set cset = {0,}; + + cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; + cset.clk_id = clk_id; + cset.clk_freq_in_hz = freq; + cset.clk_attri = attri; + cset.clk_root = clk_root; + cset.enable = !!freq; + + pr_err("DEBUG: %s: %d clk-id %x -> rate %d \n", __func__, __LINE__, + clk_id, freq); + + return q6afe_set_param(afe, NULL, &cset, AFE_PARAM_ID_CLOCK_SET, + AFE_MODULE_CLOCK_SET, sizeof(cset), + AFE_CLK_TOKEN, false); +} +EXPORT_SYMBOL_GPL(q6afe_set_lpass_clock); + int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir) @@ -980,7 +1147,7 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case LPAIF_OSR_CLK: @@ -989,11 +1156,12 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK2_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; cset.clk_id = clk_id; cset.clk_freq_in_hz = freq; @@ -1054,7 +1222,7 @@ int q6afe_port_stop(struct q6afe_port *port) stop->port_id = port_id; stop->reserved = 0; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, true); if (ret) dev_err(afe->dev, "AFE close failed %d\n", ret); @@ -1289,6 +1457,28 @@ int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg) EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare); /** + * q6afe_dam_port_prepare() - Prepare dma afe port. + * + * @port: Instance of afe port + * @cfg: DMA configuration for the afe port + * + */ +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + struct afe_param_id_cdc_dma_cfg *dma_cfg = &pcfg->dma_cfg; + + dma_cfg->cdc_dma_cfg_minor_version = AFE_API_VERSION_CODEC_DMA_CONFIG; + dma_cfg->sample_rate = cfg->sample_rate; + dma_cfg->bit_width = cfg->bit_width; + dma_cfg->data_format = cfg->data_format; + dma_cfg->num_channels = cfg->num_channels; + if (!cfg->active_channels_mask) + dma_cfg->active_channels_mask = (1 << cfg->num_channels) - 1; +} +EXPORT_SYMBOL_GPL(q6afe_cdc_dma_port_prepare); +/** * q6afe_port_start() - Start a afe port * * @port: Instance of port to start @@ -1344,7 +1534,7 @@ int q6afe_port_start(struct q6afe_port *port) start->port_id = port_id; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, true); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -1420,7 +1610,9 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: cfg_type = AFE_PARAM_ID_TDM_CONFIG; break; - + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0 ... AFE_PORT_ID_RX_CODEC_DMA_RX_7: + cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG; + break; default: dev_err(dev, "Invalid port id 0x%x\n", port_id); return ERR_PTR(-EINVAL); @@ -1458,6 +1650,83 @@ void q6afe_port_put(struct q6afe_port *port) } EXPORT_SYMBOL_GPL(q6afe_port_put); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_devote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + vote_cfg->client_handle = client_handle; + + ret = afe_apr_send_pkt(afe, pkt, NULL, true); + if (ret) + dev_err(afe->dev, "AFE close failed %d\n", ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_unvote_lpass_core_hw); + +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_vote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + strlcpy(vote_cfg->client_name, client_name, + sizeof(vote_cfg->client_name)); + + ret = afe_apr_send_pkt(afe, pkt, NULL, false); + if (ret) + dev_err(afe->dev, "AFE close failed %d\n", ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_vote_lpass_core_hw); + static int q6afe_probe(struct apr_device *adev) { struct q6afe *afe; @@ -1470,6 +1739,7 @@ static int q6afe_probe(struct apr_device *adev) q6core_get_svc_api_info(adev->svc_id, &afe->ainfo); afe->apr = adev; mutex_init(&afe->lock); + init_waitqueue_head(&afe->wait); afe->dev = dev; INIT_LIST_HEAD(&afe->port_list); spin_lock_init(&afe->port_list_lock); diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index c7ed5422baff..22e10269aa10 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -5,7 +5,7 @@ #include <dt-bindings/sound/qcom,q6afe.h> -#define AFE_PORT_MAX 105 +#define AFE_PORT_MAX 127 #define MSM_AFE_PORT_TYPE_RX 0 #define MSM_AFE_PORT_TYPE_TX 1 @@ -133,6 +133,19 @@ /* Clock ID for INT MCLK1 */ #define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK 0x309 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x30a +#define Q6AFE_LPASS_CLK_ID_TX_CORE_MCLK 0x30c +#define Q6AFE_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d +#define Q6AFE_LPASS_CLK_ID_RX_CORE_MCLK 0x30e +#define Q6AFE_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f +#define Q6AFE_LPASS_CLK_ID_VA_CORE_MCLK 0x30b +#define Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x310 + +#define Q6AFE_LPASS_CORE_AVTIMER_BLOCK 0x2 +#define Q6AFE_LPASS_CORE_HW_MACRO_BLOCK 0x3 +#define Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK 0x4 + /* Clock attribute for invalid use (reserved for internal usage) */ #define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0 /* Clock attribute for no couple case */ @@ -184,11 +197,21 @@ struct q6afe_tdm_cfg { u16 ch_mapping[AFE_MAX_CHAN_COUNT]; }; +struct q6afe_cdc_dma_cfg { + u16 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +}; + + struct q6afe_port_config { struct q6afe_hdmi_cfg hdmi; struct q6afe_slim_cfg slim; struct q6afe_i2s_cfg i2s_cfg; struct q6afe_tdm_cfg tdm; + struct q6afe_cdc_dma_cfg dma_cfg; }; struct q6afe_port; @@ -204,8 +227,16 @@ void q6afe_slim_port_prepare(struct q6afe_port *port, struct q6afe_slim_cfg *cfg); int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg); void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg); +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg); int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir); +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int clk_src, + int clk_root, unsigned int freq); +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle); #endif /* __Q6AFE_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 9b7b218f2a20..a1dd31f306ce 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -50,7 +50,7 @@ enum stream_state { struct q6asm_dai_rtd { struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; - struct snd_compr_params codec_param; + struct snd_codec codec; struct snd_dma_buffer dma_buffer; spinlock_t lock; phys_addr_t phys; @@ -64,8 +64,14 @@ struct q6asm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; + uint32_t next_track_stream_id; + bool next_track; + uint32_t stream_id; uint16_t session_id; enum stream_state state; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + bool notify_on_drain; }; struct q6asm_dai_data { @@ -181,8 +187,8 @@ static void event_handler(uint32_t opcode, uint32_t token, switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6ASM_STREAM_STOPPED; @@ -191,8 +197,8 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); break; } @@ -200,7 +206,7 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); break; default: @@ -233,7 +239,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, /* rate and channels are sent to audio driver */ if (prtd->state) { /* clear the previous setup if any */ - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6routing_stream_close(soc_prtd->dai_link->id, @@ -252,11 +258,13 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, - 0, prtd->bits_per_sample); + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, + 0, prtd->bits_per_sample, false); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, - prtd->bits_per_sample); + ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, + prtd->bits_per_sample); } if (ret < 0) { @@ -276,17 +284,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = q6asm_media_format_block_multi_ch_pcm( - prtd->audio_client, runtime->rate, - runtime->channels, NULL, + prtd->audio_client, prtd->stream_id, + runtime->rate, runtime->channels, NULL, prtd->bits_per_sample); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, - runtime->rate, runtime->channels, - prtd->bits_per_sample); + prtd->stream_id, + runtime->rate, + runtime->channels, + prtd->bits_per_sample); /* Queue the buffers */ for (i = 0; i < runtime->periods; i++) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); } if (ret < 0) @@ -308,15 +318,18 @@ static int q6asm_dai_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); break; default: ret = -EINVAL; @@ -361,6 +374,9 @@ static int q6asm_dai_open(struct snd_soc_component *component, return ret; } + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = q6asm_dai_hardware_playback; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -427,7 +443,8 @@ static int q6asm_dai_close(struct snd_soc_component *component, if (prtd->audio_client) { if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); @@ -493,14 +510,21 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, struct q6asm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; unsigned long flags; + u32 wflags = 0; uint64_t avail; + uint32_t bytes_written, bytes_to_write; + bool is_last_buffer = false; switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: spin_lock_irqsave(&prtd->lock, flags); if (!prtd->bytes_sent) { - q6asm_write_async(prtd->audio_client, prtd->pcm_count, - 0, 0, NO_TIMESTAMP); + q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->stream_id, + prtd->initial_samples_drop); + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); prtd->bytes_sent += prtd->pcm_count; } @@ -508,13 +532,37 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: - prtd->state = Q6ASM_STREAM_STOPPED; + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->notify_on_drain) { + if (substream->partial_drain) { + /* + * Close old stream and make it stale, switch + * the active stream now! + */ + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, + CMD_CLOSE); + /* + * vaild stream ids start from 1, So we are + * toggling this between 1 and 2. + */ + prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); + } + + snd_compr_drain_notify(prtd->cstream); + prtd->notify_on_drain = false; + + } else { + prtd->state = Q6ASM_STREAM_STOPPED; + } + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: spin_lock_irqsave(&prtd->lock, flags); - prtd->copied_total += prtd->pcm_count; + bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; + prtd->copied_total += bytes_written; snd_compr_fragment_elapsed(substream); if (prtd->state != Q6ASM_STREAM_RUNNING) { @@ -523,13 +571,32 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, } avail = prtd->bytes_received - prtd->bytes_sent; + if (avail > prtd->pcm_count) { + bytes_to_write = prtd->pcm_count; + } else { + if (substream->partial_drain || prtd->notify_on_drain) + is_last_buffer = true; + bytes_to_write = avail; + } - if (avail >= prtd->pcm_count) { - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); - prtd->bytes_sent += prtd->pcm_count; + if (bytes_to_write) { + if (substream->partial_drain && is_last_buffer) { + wflags |= ASM_LAST_BUFFER_FLAG; + q6asm_stream_remove_trailing_silence(prtd->audio_client, + prtd->stream_id, + prtd->trailing_samples_drop); + } + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + bytes_to_write, 0, 0, wflags); + + prtd->bytes_sent += bytes_to_write; } + if (prtd->notify_on_drain && is_last_buffer) + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, CMD_EOS); + spin_unlock_irqrestore(&prtd->lock, flags); break; @@ -560,6 +627,9 @@ static int q6asm_dai_compr_open(struct snd_soc_component *component, if (!prtd) return -ENOMEM; + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + prtd->cstream = stream; prtd->audio_client = q6asm_audio_client_alloc(dev, (q6asm_cb)compress_event_handler, @@ -606,8 +676,15 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = stream->private_data; if (prtd->audio_client) { - if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + if (prtd->state) { + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); + if (prtd->next_track_stream_id) { + q6asm_cmd(prtd->audio_client, + prtd->next_track_stream_id, + CMD_CLOSE); + } + } snd_dma_free_pages(&prtd->dma_buffer); q6asm_unmap_memory_regions(stream->direction, @@ -621,15 +698,13 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_set_params(struct snd_soc_component *component, - struct snd_compr_stream *stream, - struct snd_compr_params *params) +static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_codec *codec, + int stream_id) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = stream->private_data; - int dir = stream->direction; - struct q6asm_dai_data *pdata; struct q6asm_flac_cfg flac_cfg; struct q6asm_wma_cfg wma_cfg; struct q6asm_alac_cfg alac_cfg; @@ -643,52 +718,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, struct snd_dec_alac *alac; struct snd_dec_ape *ape; - codec_options = &(prtd->codec_param.codec.options); - - - memcpy(&prtd->codec_param, params, sizeof(*params)); - - pdata = snd_soc_component_get_drvdata(component); - if (!pdata) - return -EINVAL; - - if (!prtd || !prtd->audio_client) { - dev_err(dev, "private data null or audio client freed\n"); - return -EINVAL; - } - - prtd->periods = runtime->fragments; - prtd->pcm_count = runtime->fragment_size; - prtd->pcm_size = runtime->fragments * runtime->fragment_size; - prtd->bits_per_sample = 16; - if (dir == SND_COMPRESS_PLAYBACK) { - ret = q6asm_open_write(prtd->audio_client, params->codec.id, - params->codec.profile, prtd->bits_per_sample); - - if (ret < 0) { - dev_err(dev, "q6asm_open_write failed\n"); - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; - return ret; - } - } + codec_options = &(prtd->codec.options); - prtd->session_id = q6asm_get_session_id(prtd->audio_client); - ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, - prtd->session_id, dir); - if (ret) { - dev_err(dev, "Stream reg failed ret:%d\n", ret); - return ret; - } + memcpy(&prtd->codec, codec, sizeof(*codec)); - switch (params->codec.id) { + switch (codec->id) { case SND_AUDIOCODEC_FLAC: memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); flac = &codec_options->flac_d; - flac_cfg.ch_cfg = params->codec.ch_in; - flac_cfg.sample_rate = params->codec.sample_rate; + flac_cfg.ch_cfg = codec->ch_in; + flac_cfg.sample_rate = codec->sample_rate; flac_cfg.stream_info_present = 1; flac_cfg.sample_size = flac->sample_size; flac_cfg.min_blk_size = flac->min_blk_size; @@ -697,6 +738,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, flac_cfg.min_frame_size = flac->min_frame_size; ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + stream_id, &flac_cfg); if (ret < 0) { dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); @@ -709,10 +751,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); - wma_cfg.sample_rate = params->codec.sample_rate; - wma_cfg.num_channels = params->codec.ch_in; - wma_cfg.bytes_per_sec = params->codec.bit_rate / 8; - wma_cfg.block_align = params->codec.align; + wma_cfg.sample_rate = codec->sample_rate; + wma_cfg.num_channels = codec->ch_in; + wma_cfg.bytes_per_sec = codec->bit_rate / 8; + wma_cfg.block_align = codec->align; wma_cfg.bits_per_sample = prtd->bits_per_sample; wma_cfg.enc_options = wma->encoder_option; wma_cfg.adv_enc_options = wma->adv_encoder_option; @@ -726,7 +768,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return -EINVAL; /* check the codec profile */ - switch (params->codec.profile) { + switch (codec->profile) { case SND_AUDIOPROFILE_WMA9: wma_cfg.fmtag = 0x161; wma_v9 = 1; @@ -750,16 +792,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, default: dev_err(dev, "Unknown WMA profile:%x\n", - params->codec.profile); + codec->profile); return -EIO; } if (wma_v9) ret = q6asm_stream_media_format_block_wma_v9( - prtd->audio_client, &wma_cfg); + prtd->audio_client, stream_id, + &wma_cfg); else ret = q6asm_stream_media_format_block_wma_v10( - prtd->audio_client, &wma_cfg); + prtd->audio_client, stream_id, + &wma_cfg); if (ret < 0) { dev_err(dev, "WMA9 CMD failed:%d\n", ret); return -EIO; @@ -770,10 +814,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&alac_cfg, 0x0, sizeof(alac_cfg)); alac = &codec_options->alac_d; - alac_cfg.sample_rate = params->codec.sample_rate; - alac_cfg.avg_bit_rate = params->codec.bit_rate; + alac_cfg.sample_rate = codec->sample_rate; + alac_cfg.avg_bit_rate = codec->bit_rate; alac_cfg.bit_depth = prtd->bits_per_sample; - alac_cfg.num_channels = params->codec.ch_in; + alac_cfg.num_channels = codec->ch_in; alac_cfg.frame_length = alac->frame_length; alac_cfg.pb = alac->pb; @@ -783,7 +827,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, alac_cfg.compatible_version = alac->compatible_version; alac_cfg.max_frame_bytes = alac->max_frame_bytes; - switch (params->codec.ch_in) { + switch (codec->ch_in) { case 1: alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; break; @@ -792,6 +836,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } ret = q6asm_stream_media_format_block_alac(prtd->audio_client, + stream_id, &alac_cfg); if (ret < 0) { dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); @@ -803,8 +848,8 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&ape_cfg, 0x0, sizeof(ape_cfg)); ape = &codec_options->ape_d; - ape_cfg.sample_rate = params->codec.sample_rate; - ape_cfg.num_channels = params->codec.ch_in; + ape_cfg.sample_rate = codec->sample_rate; + ape_cfg.num_channels = codec->ch_in; ape_cfg.bits_per_sample = prtd->bits_per_sample; ape_cfg.compatible_version = ape->compatible_version; @@ -816,6 +861,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, ape_cfg.seek_table_present = ape->seek_table_present; ret = q6asm_stream_media_format_block_ape(prtd->audio_client, + stream_id, &ape_cfg); if (ret < 0) { dev_err(dev, "APE CMD Format block failed:%d\n", ret); @@ -827,6 +873,64 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } + return 0; +} + +static int q6asm_dai_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + int dir = stream->direction; + struct q6asm_dai_data *pdata; + struct device *dev = component->dev; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + dev_err(dev, "private data null or audio client freed\n"); + return -EINVAL; + } + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + + if (dir == SND_COMPRESS_PLAYBACK) { + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, + params->codec.profile, prtd->bits_per_sample, + true); + + if (ret < 0) { + dev_err(dev, "q6asm_open_write failed\n"); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return ret; + } + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, dir); + if (ret) { + dev_err(dev, "Stream reg failed ret:%d\n", ret); + return ret; + } + + ret = __q6asm_dai_compr_set_codec_params(component, stream, + ¶ms->codec, + prtd->stream_id); + if (ret) { + dev_err(dev, "codec param setup failed ret:%d\n", ret); + return ret; + } + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, (prtd->pcm_size / prtd->periods), prtd->periods); @@ -841,6 +945,55 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return 0; } +static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_ENCODER_PADDING: + prtd->trailing_samples_drop = metadata->value[0]; + break; + case SNDRV_COMPRESS_ENCODER_DELAY: + prtd->initial_samples_drop = metadata->value[0]; + if (prtd->next_track_stream_id) { + ret = q6asm_open_write(prtd->audio_client, + prtd->next_track_stream_id, + prtd->codec.id, + prtd->codec.profile, + prtd->bits_per_sample, + true); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + ret = __q6asm_dai_compr_set_codec_params(component, stream, + &prtd->codec, + prtd->next_track_stream_id); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + + ret = q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->next_track_stream_id, + prtd->initial_samples_drop); + prtd->next_track_stream_id = 0; + + } + + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int q6asm_dai_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *stream, int cmd) { @@ -852,15 +1005,26 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + prtd->next_track = true; + prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + prtd->notify_on_drain = true; break; default: ret = -EINVAL; @@ -888,16 +1052,71 @@ static int q6asm_dai_compr_pointer(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_ack(struct snd_soc_component *component, - struct snd_compr_stream *stream, - size_t count) +static int q6asm_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; unsigned long flags; + u32 wflags = 0; + int avail, bytes_in_flight = 0; + void *dstn; + size_t copy; + u32 app_pointer; + u32 bytes_received; + + bytes_received = prtd->bytes_received; + + /** + * Make sure that next track data pointer is aligned at 32 bit boundary + * This is a Mandatory requirement from DSP data buffers alignment + */ + if (prtd->next_track) + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + + app_pointer = bytes_received/prtd->pcm_size; + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + dstn = prtd->dma_buffer.area + app_pointer; + + if (count < prtd->pcm_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = prtd->pcm_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->dma_buffer.area, buf + copy, + count - copy)) + return -EFAULT; + } spin_lock_irqsave(&prtd->lock, flags); - prtd->bytes_received += count; + + bytes_in_flight = prtd->bytes_received - prtd->copied_total; + + if (prtd->next_track) { + prtd->next_track = false; + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); + } + + prtd->bytes_received = bytes_received + count; + + /* Kick off the data to dsp if its starving!! */ + if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { + uint32_t bytes_to_write = prtd->pcm_count; + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail < prtd->pcm_count) + bytes_to_write = avail; + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + bytes_to_write, 0, 0, wflags); + prtd->bytes_sent += bytes_to_write; + } + spin_unlock_irqrestore(&prtd->lock, flags); return count; @@ -954,12 +1173,13 @@ static struct snd_compress_ops q6asm_dai_compress_ops = { .open = q6asm_dai_compr_open, .free = q6asm_dai_compr_free, .set_params = q6asm_dai_compr_set_params, + .set_metadata = q6asm_dai_compr_set_metadata, .pointer = q6asm_dai_compr_pointer, .trigger = q6asm_dai_compr_trigger, .get_caps = q6asm_dai_compr_get_caps, .get_codec_caps = q6asm_dai_compr_get_codec_caps, .mmap = q6asm_dai_compr_mmap, - .ack = q6asm_dai_compr_ack, + .copy = q6asm_compr_copy, }; static int q6asm_dai_pcm_new(struct snd_soc_component *component, diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 755062eadcc8..d745a02fcd5f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -51,6 +51,8 @@ #define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D #define ASM_MEDIA_FMT_ALAC 0x00012f31 #define ASM_MEDIA_FMT_APE 0x00012f32 +#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 +#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 #define ASM_LEGACY_STREAM_SESSION 0 @@ -270,7 +272,6 @@ struct audio_client { wait_queue_head_t cmd_wait; struct aprv2_ibasic_rsp_result_t result; int perf_mode; - int stream_id; struct q6asm *q6asm; struct device *dev; }; @@ -640,6 +641,8 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, case ASM_STREAM_CMD_OPEN_READWRITE_V2: case ASM_STREAM_CMD_SET_ENCDEC_PARAM: case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: if (result->status != 0) { dev_err(ac->dev, "cmd = 0x%x returned error = 0x%x\n", @@ -671,6 +674,7 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, if (ac->io_mode & ASM_SYNC_IO_MODE) { phys_addr_t phys; unsigned long flags; + int token = hdr->token & ASM_WRITE_TOKEN_MASK; spin_lock_irqsave(&ac->lock, flags); @@ -682,12 +686,12 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, goto done; } - phys = port->buf[hdr->token].phys; + phys = port->buf[token].phys; if (lower_32_bits(phys) != result->opcode || upper_32_bits(phys) != result->status) { dev_err(ac->dev, "Expected addr %pa\n", - &port->buf[hdr->token].phys); + &port->buf[token].phys); spin_unlock_irqrestore(&ac->lock, flags); ret = -EINVAL; goto done; @@ -828,21 +832,21 @@ EXPORT_SYMBOL_GPL(q6asm_get_session_id); * @dev: Pointer to asm child device. * @cb: event callback. * @priv: private data associated with this client. - * @stream_id: stream id + * @session_id: session id * @perf_mode: performace mode for this client * * Return: Will be an error pointer on error or a valid audio client * on success. */ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, - void *priv, int stream_id, + void *priv, int session_id, int perf_mode) { struct q6asm *a = dev_get_drvdata(dev->parent); struct audio_client *ac; unsigned long flags; - ac = q6asm_get_audio_client(a, stream_id + 1); + ac = q6asm_get_audio_client(a, session_id + 1); if (ac) { dev_err(dev, "Audio Client already active\n"); return ac; @@ -853,17 +857,15 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, return ERR_PTR(-ENOMEM); spin_lock_irqsave(&a->slock, flags); - a->session[stream_id + 1] = ac; + a->session[session_id + 1] = ac; spin_unlock_irqrestore(&a->slock, flags); - ac->session = stream_id + 1; + ac->session = session_id + 1; ac->cb = cb; ac->dev = dev; ac->q6asm = a; ac->priv = priv; ac->io_mode = ASM_SYNC_IO_MODE; ac->perf_mode = perf_mode; - /* DSP expects stream id from 1 */ - ac->stream_id = 1; ac->adev = a->adev; kref_init(&ac->refcount); @@ -919,8 +921,9 @@ err: * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample) +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, + uint16_t bits_per_sample, bool is_gapless) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; @@ -935,11 +938,13 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; open->mode_flags = 0x00; open->mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless) + open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG); /* source endpoint : matrix */ open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; @@ -998,8 +1003,9 @@ err: } EXPORT_SYMBOL_GPL(q6asm_open_write); -static int __q6asm_run(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts, bool wait) +static int __q6asm_run(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts, + bool wait) { struct asm_session_cmd_run_v2 *run; struct apr_pkt *pkt; @@ -1014,7 +1020,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, pkt = p; run = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; run->flags = flags; @@ -1042,10 +1048,10 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, * * Return: Will be an negative value on error or zero on success */ -int q6asm_run(struct audio_client *ac, uint32_t flags, +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true); } EXPORT_SYMBOL_GPL(q6asm_run); @@ -1053,16 +1059,17 @@ EXPORT_SYMBOL_GPL(q6asm_run); * q6asm_run_nowait() - start the audio client withou blocking * * @ac: audio client pointer + * @stream_id: stream id * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */ -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts) +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false); } EXPORT_SYMBOL_GPL(q6asm_run_nowait); @@ -1070,6 +1077,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @channel_map: channel map pointer @@ -1078,6 +1086,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * Return: Will be an negative value on error or zero on success */ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample) @@ -1096,7 +1105,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1125,8 +1134,8 @@ err: } EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); - int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg) { struct asm_flac_fmt_blk_v2 *fmt; @@ -1142,7 +1151,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1163,6 +1172,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmastdv9_fmt_blk_v2 *fmt; @@ -1178,7 +1188,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1200,6 +1210,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmaprov10_fmt_blk_v2 *fmt; @@ -1215,7 +1226,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1238,6 +1249,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg) { struct asm_alac_fmt_blk_v2 *fmt; @@ -1253,7 +1265,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1279,6 +1291,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg) { struct asm_ape_fmt_blk_v2 *fmt; @@ -1294,7 +1307,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1317,10 +1330,60 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, } EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); +static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t cmd, + uint32_t num_samples) +{ + uint32_t *samples; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(uint32_t); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + samples = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); + + pkt->hdr.opcode = cmd; + *samples = num_samples; + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + + kfree(pkt); + + return rc; +} + +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_INITIAL_SILENCE, + initial_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence); + +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t trailing_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_TRAILING_SILENCE, + trailing_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence); + /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @bits_per_sample: bits per sample @@ -1328,7 +1391,9 @@ EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); * Return: Will be an negative value on error or zero on success */ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample) + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample) { struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; struct apr_pkt *pkt; @@ -1344,7 +1409,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, pkt = p; enc_cfg = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; @@ -1376,10 +1441,11 @@ EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); * q6asm_read() - read data of period size from audio client * * @ac: audio client pointer + * @stream_id: stream id * * Return: Will be an negative value on error or zero on success */ -int q6asm_read(struct audio_client *ac) +int q6asm_read(struct audio_client *ac, uint32_t stream_id) { struct asm_data_cmd_read_v2 *read; struct audio_port_data *port; @@ -1400,7 +1466,7 @@ int q6asm_read(struct audio_client *ac) spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; read->buf_addr_lsw = lower_32_bits(ab->phys); @@ -1428,7 +1494,7 @@ int q6asm_read(struct audio_client *ac) } EXPORT_SYMBOL_GPL(q6asm_read); -static int __q6asm_open_read(struct audio_client *ac, +static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id, uint32_t format, uint16_t bits_per_sample) { struct asm_stream_cmd_open_read_v3 *open; @@ -1444,7 +1510,7 @@ static int __q6asm_open_read(struct audio_client *ac, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; /* Stream prio : High, provide meta info with encoded frames */ open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; @@ -1475,15 +1541,16 @@ static int __q6asm_open_read(struct audio_client *ac, * q6asm_open_read() - Open audio client for reading * * @ac: audio client pointer + * @stream_id: stream id * @format: audio sample format * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_read(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample) +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample) { - return __q6asm_open_read(ac, format, bits_per_sample); + return __q6asm_open_read(ac, stream_id, format, bits_per_sample); } EXPORT_SYMBOL_GPL(q6asm_open_read); @@ -1491,6 +1558,7 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * q6asm_write_async() - non blocking write * * @ac: audio client pointer + * @stream_id: stream id * @len: length in bytes * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw @@ -1498,8 +1566,8 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * * Return: Will be an negative value on error or zero on success */ -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t wflags) +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) { struct asm_data_cmd_write_v2 *write; struct audio_port_data *port; @@ -1520,10 +1588,10 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; - pkt->hdr.token = port->dsp_buf; + pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT); pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; write->buf_addr_lsw = lower_32_bits(ab->phys); write->buf_addr_msw = upper_32_bits(ab->phys); @@ -1534,10 +1602,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, write->mem_map_handle = ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; - if (wflags == NO_TIMESTAMP) - write->flags = (wflags & 0x800000FF); - else - write->flags = (0x80000000 | wflags); + write->flags = wflags; port->dsp_buf++; @@ -1567,9 +1632,9 @@ static void q6asm_reset_buf_state(struct audio_client *ac) spin_unlock_irqrestore(&ac->lock, flags); } -static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) +static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd, + bool wait) { - int stream_id = ac->stream_id; struct apr_pkt pkt; int rc; @@ -1616,13 +1681,14 @@ static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) * q6asm_cmd() - run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd(struct audio_client *ac, int cmd) +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, true); + return __q6asm_cmd(ac, stream_id, cmd, true); } EXPORT_SYMBOL_GPL(q6asm_cmd); @@ -1630,13 +1696,14 @@ EXPORT_SYMBOL_GPL(q6asm_cmd); * q6asm_cmd_nowait() - non blocking, run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, false); + return __q6asm_cmd(ac, stream_id, cmd, false); } EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 38a207d6cd95..82e584aa534f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -20,6 +20,9 @@ #define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008 #define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 #define ASM_CLIENT_EVENT_DATA_READ_DONE 0x100a +#define ASM_WRITE_TOKEN_MASK GENMASK(15, 0) +#define ASM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16) +#define ASM_WRITE_TOKEN_LEN_SHIFT 16 enum { LEGACY_PCM_MODE = 0, @@ -29,8 +32,8 @@ enum { }; #define MAX_SESSIONS 8 -#define NO_TIMESTAMP 0xFF00 #define FORMAT_LINEAR_PCM 0x0000 +#define ASM_LAST_BUFFER_FLAG BIT(30) struct q6asm_flac_cfg { u32 sample_rate; @@ -93,37 +96,53 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, void *priv, int session_id, int perf_mode); void q6asm_audio_client_free(struct audio_client *ac); -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t flags); -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample); +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags); +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, + uint16_t bits_per_sample, bool is_gapless); -int q6asm_open_read(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample); +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample); int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample); -int q6asm_read(struct audio_client *ac); + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_read(struct audio_client *ac, uint32_t stream_id); int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample); int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg); -int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_cmd(struct audio_client *ac, int cmd); -int q6asm_cmd_nowait(struct audio_client *ac, int cmd); +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples); +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t trailing_samples); +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd); +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd); int q6asm_get_session_id(struct audio_client *ac); int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 25d23e0266c7..b12539fae6ed 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -113,7 +113,19 @@ { mix_name, "QUIN_TDM_TX_4", "QUIN_TDM_TX_4"}, \ { mix_name, "QUIN_TDM_TX_5", "QUIN_TDM_TX_5"}, \ { mix_name, "QUIN_TDM_TX_6", "QUIN_TDM_TX_6"}, \ - { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"} + { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"}, \ + { mix_name, "WSA_CODEC_DMA_TX_0", "WSA_CODEC_DMA_TX_0"}, \ + { mix_name, "WSA_CODEC_DMA_TX_1", "WSA_CODEC_DMA_TX_1"}, \ + { mix_name, "WSA_CODEC_DMA_TX_2", "WSA_CODEC_DMA_TX_2"}, \ + { mix_name, "VA_CODEC_DMA_TX_0", "VA_CODEC_DMA_TX_0"}, \ + { mix_name, "VA_CODEC_DMA_TX_1", "VA_CODEC_DMA_TX_1"}, \ + { mix_name, "VA_CODEC_DMA_TX_2", "VA_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_0", "TX_CODEC_DMA_TX_0"}, \ + { mix_name, "TX_CODEC_DMA_TX_1", "TX_CODEC_DMA_TX_1"}, \ + { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ + { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ + { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} #define Q6ROUTING_TX_MIXERS(id) \ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ @@ -268,6 +280,42 @@ msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("QUIN_TDM_TX_7", QUINARY_TDM_TX_7, \ id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_0", WSA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_1", WSA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_2", WSA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_0", VA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_1", VA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_2", VA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_0", TX_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_1", TX_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_2", TX_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_3", TX_CODEC_DMA_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_4", TX_CODEC_DMA_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), struct session_data { @@ -609,6 +657,36 @@ static const struct snd_kcontrol_new quin_tdm_rx_6_mixer_controls[] = { static const struct snd_kcontrol_new quin_tdm_rx_7_mixer_controls[] = { Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_7) }; +static const struct snd_kcontrol_new wsa_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new wsa_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_2) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_3) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_4) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_5) }; + +static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_6) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; + static const struct snd_kcontrol_new mmul1_mixer_controls[] = { Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; @@ -819,6 +897,37 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("QUIN_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, quin_tdm_rx_7_mixer_controls, ARRAY_SIZE(quin_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_2_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_3_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_4_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_5_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + rxcodec_dma_rx_6_mixer_controls, + ARRAY_SIZE(rxcodec_dma_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_7_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_7_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, @@ -901,6 +1010,16 @@ static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_5 Audio Mixer", "QUIN_TDM_RX_5"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_6 Audio Mixer", "QUIN_TDM_RX_6"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_7 Audio Mixer", "QUIN_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_0 Audio Mixer", "WSA_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_1 Audio Mixer", "WSA_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_0 Audio Mixer", "RX_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_1 Audio Mixer", "RX_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_2 Audio Mixer", "RX_CODEC_DMA_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_3 Audio Mixer", "RX_CODEC_DMA_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_4 Audio Mixer", "RX_CODEC_DMA_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_5 Audio Mixer", "RX_CODEC_DMA_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c new file mode 100644 index 000000000000..436da2ad6f1b --- /dev/null +++ b/sound/soc/qcom/sm8250.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020, Linaro Limited + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <linux/soundwire/sdw.h> +#include "qdsp6/q6afe.h" +#include "common.h" + +#define MI2S_BCLK_RATE 1536000 +#define DEFAULT_MCLK_RATE 24576000 +#define MAX_WSA_PORTS 20 +struct sm8250_snd_data { + bool stream_prepared;//[MAX_WSA_PORTS]; + struct snd_soc_card *card; + struct sdw_stream_runtime *sruntime;//[MAX_WSA_PORTS]; +}; + +static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int sm8250_snd_startup(struct snd_pcm_substream *substream) +{ + unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + + switch (cpu_dai->id) { + case TERTIARY_MI2S_RX: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF; + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); + break; + default: + break; + } + return 0; +} + +static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + struct sm8250_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime; + int i; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + sruntime = snd_soc_dai_get_sdw_stream(codec_dai, + substream->stream); + if (sruntime != ERR_PTR(-ENOTSUPP)) + pdata->sruntime/*[cpu_dai->id]*/ = sruntime; + } + + return 0; + +} + +static int sm8250_snd_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime;//[cpu_dai->id]; + int ret; + + if (!sruntime) + return 0; + + if (data->stream_prepared/*[cpu_dai->id]*/) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared/*[cpu_dai->id]*/ = false; + } + + ret = sdw_prepare_stream(sruntime); + if (ret) + return ret; + + /** + * NOTE: there is a strict hw requirement about the ordering of port + * enables and actual WSA881x PA enable. PA enable should only happen + * after soundwire ports are enabled if not DC on the line is + * accumulated resulting in Click/Pop Noise + * PA enable/mute are handled as part of codec DAPM and digital mute. + */ + + ret = sdw_enable_stream(sruntime); + if (ret) { + sdw_deprepare_stream(sruntime); + return ret; + } + data->stream_prepared/*[cpu_dai->id] */ = true; + + return ret; +} + +static int sm8250_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct sdw_stream_runtime *sruntime = data->sruntime;//[cpu_dai->id]; + + if (sruntime && data->stream_prepared/*[cpu_dai->id]*/) { + sdw_disable_stream(sruntime); + sdw_deprepare_stream(sruntime); + data->stream_prepared/*[cpu_dai->id]*/ = false; + } + + return 0; +} + +static const struct snd_soc_ops sm8250_be_ops = { + .startup = sm8250_snd_startup, + .hw_params = sm8250_snd_hw_params, + .hw_free = sm8250_snd_hw_free, + .prepare = sm8250_snd_prepare, +}; + +static void sm8250_add_be_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm == 1) { + link->be_hw_params_fixup = sm8250_be_hw_params_fixup; + link->ops = &sm8250_be_ops; + } + } +} + +static int sm8250_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card; + struct sm8250_snd_data *data; + struct device *dev = &pdev->dev; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + /* Allocate the private data */ + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + card->dev = dev; + dev_set_drvdata(dev, card); + snd_soc_card_set_drvdata(card, data); + ret = qcom_snd_parse_of(card); + if (ret) + return ret; + + sm8250_add_be_ops(card); + return devm_snd_soc_register_card(dev, card); +} + +static const struct of_device_id snd_sm8250_dt_match[] = { + {.compatible = "qcom,sm8250-sndcard"}, + {} +}; + +MODULE_DEVICE_TABLE(of, snd_sm8250_dt_match); + +static struct platform_driver snd_sm8250_driver = { + .probe = sm8250_platform_probe, + .driver = { + .name = "snd-sm8250", + .of_match_table = snd_sm8250_dt_match, + }, +}; +module_platform_driver(snd_sm8250_driver); +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_DESCRIPTION("SM8250 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); |