From 989fccf8757d6d31841d9c43f7680bdb4b29218a Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Fri, 15 Jun 2018 16:53:31 +0530 Subject: [PATCH 1/2] asoc: bolero: Add support for TX macro driver Add support for TX digital portion to register as macro to bolero codec. ASoC dapm and dai functionality of TX macro is associated to bolero codec itself. Change-Id: I1e222610d1750eccd6822d9901b17ec2ea509389 Signed-off-by: Laxminath Kasam --- asoc/codecs/bolero/tx-macro.c | 1712 +++++++++++++++++++++++++++++++++ 1 file changed, 1712 insertions(+) create mode 100644 asoc/codecs/bolero/tx-macro.c diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c new file mode 100644 index 000000000000..0795d47c8138 --- /dev/null +++ b/asoc/codecs/bolero/tx-macro.c @@ -0,0 +1,1712 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" +#include "../msm-cdc-pinctrl.h" + +#define TX_MACRO_MAX_OFFSET 0x1000 + +#define NUM_DECIMATORS 8 + +#define TX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define TX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED 0 +#define TX_MACRO_MCLK_FREQ 9600000 +#define TX_MACRO_TX_PATH_OFFSET 0x80 + +#define TX_MACRO_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = TX_MACRO_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int tx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); + +#define TX_MACRO_SWR_STRING_LEN 80 +#define TX_MACRO_CHILD_DEVICES_MAX 3 + +/* Hold instance to soundwire platform device */ +struct tx_macro_swr_ctrl_data { + struct platform_device *tx_swr_pdev; +}; + +struct tx_macro_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +enum { + TX_MACRO_AIF1_CAP = 0, + TX_MACRO_AIF2_CAP, + TX_MACRO_MAX_DAIS +}; + +enum { + TX_MACRO_DEC0, + TX_MACRO_DEC1, + TX_MACRO_DEC2, + TX_MACRO_DEC3, + TX_MACRO_DEC4, + TX_MACRO_DEC5, + TX_MACRO_DEC6, + TX_MACRO_DEC7, + TX_MACRO_DEC_MAX, +}; + +enum { + TX_MACRO_CLK_DIV_2, + TX_MACRO_CLK_DIV_3, + TX_MACRO_CLK_DIV_4, + TX_MACRO_CLK_DIV_6, + TX_MACRO_CLK_DIV_8, + TX_MACRO_CLK_DIV_16, +}; + +struct tx_mute_work { + struct tx_macro_priv *tx_priv; + u32 decimator; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tx_macro_priv *tx_priv; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tx_macro_priv { + struct device *dev; + bool dec_active[NUM_DECIMATORS]; + int tx_mclk_users; + int swr_clk_users; + struct clk *tx_core_clk; + struct clk *tx_npl_clk; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + struct snd_soc_codec *codec; + struct device_node *tx_swr_gpio_p; + struct tx_macro_swr_ctrl_data *swr_ctrl_data; + struct tx_macro_swr_ctrl_platform_data swr_plat_data; + struct work_struct tx_macro_add_child_devices_work; + struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + u16 dmic_clk_div; + unsigned long active_ch_mask[TX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS]; + char __iomem *tx_io_base; + struct platform_device *pdev_child_devices + [TX_MACRO_CHILD_DEVICES_MAX]; + int child_count; +}; + +static bool tx_macro_get_data(struct snd_soc_codec *codec, + struct device **tx_dev, + struct tx_macro_priv **tx_priv, + const char *func_name) +{ + *tx_dev = bolero_get_device_ptr(codec->dev, TX_MACRO); + if (!(*tx_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + + *tx_priv = dev_get_drvdata((*tx_dev)); + if (!(*tx_priv)) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + + if (!(*tx_priv)->codec) { + dev_err(codec->dev, + "%s: tx_priv->codec not initialized!\n", func_name); + return false; + } + + return true; +} + +static int tx_macro_mclk_enable(struct tx_macro_priv *tx_priv, + bool mclk_enable) +{ + struct regmap *regmap = dev_get_regmap(tx_priv->dev->parent, NULL); + int ret = 0; + + dev_dbg(tx_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, tx_priv->tx_mclk_users); + + mutex_lock(&tx_priv->mclk_lock); + if (mclk_enable) { + if (tx_priv->tx_mclk_users == 0) { + ret = bolero_request_clock(tx_priv->dev, + TX_MACRO, MCLK_MUX0, true); + if (ret < 0) { + dev_err(tx_priv->dev, + "%s: request clock enable failed\n", + __func__); + goto exit; + } + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + TX_START_OFFSET, + TX_MAX_OFFSET); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, + BOLERO_CDC_TX_TOP_CSR_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + } + tx_priv->tx_mclk_users++; + } else { + if (tx_priv->tx_mclk_users <= 0) { + dev_err(tx_priv->dev, "%s: clock already disabled\n", + __func__); + tx_priv->tx_mclk_users = 0; + goto exit; + } + tx_priv->tx_mclk_users--; + if (tx_priv->tx_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + bolero_request_clock(tx_priv->dev, + TX_MACRO, MCLK_MUX0, false); + } + } +exit: + mutex_unlock(&tx_priv->mclk_lock); + return ret; +} + +static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + dev_dbg(tx_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tx_macro_mclk_enable(tx_priv, 1); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tx_macro_mclk_enable(tx_priv, 0); + break; + default: + dev_err(tx_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int tx_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct tx_macro_priv *tx_priv = dev_get_drvdata(dev); + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(tx_priv->tx_core_clk); + if (ret < 0) { + dev_err(dev, "%s:tx mclk enable failed\n", __func__); + goto exit; + } + ret = clk_prepare_enable(tx_priv->tx_npl_clk); + if (ret < 0) { + dev_err(dev, "%s:tx npl_clk enable failed\n", + __func__); + clk_disable_unprepare(tx_priv->tx_core_clk); + goto exit; + } + } else { + clk_disable_unprepare(tx_priv->tx_npl_clk); + clk_disable_unprepare(tx_priv->tx_core_clk); + } + +exit: + return ret; +} + +static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work = NULL; + struct hpf_work *hpf_work = NULL; + struct tx_macro_priv *tx_priv = NULL; + struct snd_soc_codec *codec = NULL; + u16 dec_cfg_reg = 0; + u8 hpf_cut_off_freq = 0; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tx_priv = hpf_work->tx_priv; + codec = tx_priv->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = BOLERO_CDC_TX0_TX_PATH_CFG0 + + TX_MACRO_TX_PATH_OFFSET * hpf_work->decimator; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); +} + +static void tx_macro_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork = NULL; + struct snd_soc_codec *codec = NULL; + struct tx_macro_priv *tx_priv = NULL; + struct delayed_work *delayed_work = NULL; + u16 tx_vol_ctl_reg = 0, hpf_gate_reg = 0; + u8 decimator = 0; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tx_priv = tx_mute_dwork->tx_priv; + codec = tx_priv->codec; + decimator = tx_mute_dwork->decimator; + + tx_vol_ctl_reg = + BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = BOLERO_CDC_TX0_TX_PATH_SEC2 + + TX_MACRO_TX_PATH_OFFSET * decimator; + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + dev_dbg(tx_priv->dev, "%s: decimator %u unmute\n", + __func__, decimator); +} + +static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = 0; + u16 mic_sel_reg = 0; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = BOLERO_CDC_TX0_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = BOLERO_CDC_TX1_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = BOLERO_CDC_TX2_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = BOLERO_CDC_TX3_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = BOLERO_CDC_TX4_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = BOLERO_CDC_TX5_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = BOLERO_CDC_TX6_TX_PATH_CFG0; + break; + case BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = BOLERO_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + if (strnstr(widget->name, "smic", strlen(widget->name))) { + if (val != 0) { + if (val < 5) + snd_soc_update_bits(codec, mic_sel_reg, + 1 << 7, 0x0 << 7); + else + snd_soc_update_bits(codec, mic_sel_reg, + 1 << 7, 0x1 << 7); + } + } else { + /* DMIC selected */ + if (val != 0) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, 1 << 7); + } + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tx_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + if (test_bit(dec_id, &tx_priv->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 dec_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + if (enable) { + set_bit(dec_id, &tx_priv->active_ch_mask[dai_id]); + tx_priv->active_ch_cnt[dai_id]++; + } else { + tx_priv->active_ch_cnt[dai_id]--; + clear_bit(dec_id, &tx_priv->active_ch_mask[dai_id]); + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int tx_macro_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg = 0; + s32 *dmic_clk_cnt = NULL; + unsigned int dmic = 0; + int ret = 0; + char *wname = NULL; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + wname = strpbrk(w->name, "01234567"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tx_priv->dmic_0_1_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tx_priv->dmic_2_3_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tx_priv->dmic_4_5_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC2_CTL; + break; + case 6: + case 7: + dmic_clk_cnt = &(tx_priv->dmic_6_7_clk_cnt); + dmic_clk_reg = BOLERO_CDC_VA_TOP_CSR_DMIC3_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, tx_priv->dmic_clk_div << 0x1); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + + return 0; +} + +static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator = 0; + u16 tx_vol_ctl_reg = 0; + u16 dec_cfg_reg = 0; + u16 hpf_gate_reg = 0; + u16 tx_gain_ctl_reg = 0; + u8 hpf_cut_off_freq = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + decimator = w->shift; + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = BOLERO_CDC_TX0_TX_PATH_SEC2 + + TX_MACRO_TX_PATH_OFFSET * decimator; + dec_cfg_reg = BOLERO_CDC_TX0_TX_PATH_CFG0 + + TX_MACRO_TX_PATH_OFFSET * decimator; + tx_gain_ctl_reg = BOLERO_CDC_TX0_TX_VOL_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tx_priv->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tx_priv->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tx_priv->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tx_priv->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + } + } + cancel_delayed_work_sync( + &tx_priv->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + } + return 0; +} + +static int tx_macro_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} + +static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int tx_fs_rate = -EINVAL; + struct snd_soc_codec *codec = dai->codec; + u32 decimator = 0; + u16 sample_rate = 0; + u16 tx_fs_reg = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(codec->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + for_each_set_bit(decimator, &tx_priv->active_ch_mask[dai->id], + TX_MACRO_DEC_MAX) { + if (decimator >= 0) { + tx_fs_reg = BOLERO_CDC_TX0_TX_PATH_CTL + + TX_MACRO_TX_PATH_OFFSET * decimator; + dev_dbg(codec->dev, "%s: set DEC%u rate to %u\n", + __func__, decimator, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate); + } else { + dev_err(codec->dev, + "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + switch (dai->id) { + case TX_MACRO_AIF1_CAP: + case TX_MACRO_AIF2_CAP: + *tx_slot = tx_priv->active_ch_mask[dai->id]; + *tx_num = tx_priv->active_ch_cnt[dai->id]; + break; + default: + dev_err(tx_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static struct snd_soc_dai_ops tx_macro_dai_ops = { + .hw_params = tx_macro_hw_params, + .get_channel_map = tx_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver tx_macro_dai[] = { + { + .name = "tx_macro_tx1", + .id = TX_MACRO_AIF1_CAP, + .capture = { + .stream_name = "TX_AIF1 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, + { + .name = "tx_macro_tx2", + .id = TX_MACRO_AIF2_CAP, + .capture = { + .stream_name = "TX_AIF2 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, +}; + +#define STRING(name) #name +#define TX_MACRO_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define TX_MACRO_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define TX_MACRO_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +static const char * const adc_mux_text[] = { + "MSM_DMIC", "SWR_MIC", "ANC_FB_TUNE1" +}; + +TX_MACRO_DAPM_ENUM(tx_dec0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG1, + 0, adc_mux_text); +TX_MACRO_DAPM_ENUM(tx_dec7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG1, + 0, adc_mux_text); + + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_dmic7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 4, dmic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +static const char * const smic_mux_text[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", + "SWR_DMIC0", "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", + "SWR_DMIC4", "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +TX_MACRO_DAPM_ENUM_EXT(tx_smic0, BOLERO_CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic1, BOLERO_CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic2, BOLERO_CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic3, BOLERO_CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic4, BOLERO_CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic5, BOLERO_CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic6, BOLERO_CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +TX_MACRO_DAPM_ENUM_EXT(tx_smic7, BOLERO_CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text, snd_soc_dapm_get_enum_double, + tx_macro_put_dec_enum); + +static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new tx_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP", "TX_AIF1 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP", "TX_AIF2 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_MIXER("TX_AIF1_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0, + tx_aif1_cap_mixer, ARRAY_SIZE(tx_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("TX_AIF2_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0, + tx_aif2_cap_mixer, ARRAY_SIZE(tx_aif2_cap_mixer)), + + + TX_MACRO_DAPM_MUX("TX DMIC MUX0", 0, tx_dmic0), + TX_MACRO_DAPM_MUX("TX DMIC MUX1", 0, tx_dmic1), + TX_MACRO_DAPM_MUX("TX DMIC MUX2", 0, tx_dmic2), + TX_MACRO_DAPM_MUX("TX DMIC MUX3", 0, tx_dmic3), + TX_MACRO_DAPM_MUX("TX DMIC MUX4", 0, tx_dmic4), + TX_MACRO_DAPM_MUX("TX DMIC MUX5", 0, tx_dmic5), + TX_MACRO_DAPM_MUX("TX DMIC MUX6", 0, tx_dmic6), + TX_MACRO_DAPM_MUX("TX DMIC MUX7", 0, tx_dmic7), + + TX_MACRO_DAPM_MUX("TX SMIC MUX0", 0, tx_smic0), + TX_MACRO_DAPM_MUX("TX SMIC MUX1", 0, tx_smic1), + TX_MACRO_DAPM_MUX("TX SMIC MUX2", 0, tx_smic2), + TX_MACRO_DAPM_MUX("TX SMIC MUX3", 0, tx_smic3), + TX_MACRO_DAPM_MUX("TX SMIC MUX4", 0, tx_smic4), + TX_MACRO_DAPM_MUX("TX SMIC MUX5", 0, tx_smic5), + TX_MACRO_DAPM_MUX("TX SMIC MUX6", 0, tx_smic6), + TX_MACRO_DAPM_MUX("TX SMIC MUX7", 0, tx_smic7), + + SND_SOC_DAPM_MICBIAS_E("TX MIC BIAS1", SND_SOC_NOPM, 0, 0, + tx_macro_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("TX DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC6", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("TX DMIC7", NULL, SND_SOC_NOPM, 0, 0, + tx_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), + + SND_SOC_DAPM_MUX_E("TX DEC0 MUX", BOLERO_CDC_TX0_TX_PATH_CTL, + TX_MACRO_DEC0, 0, + &tx_dec0_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC1 MUX", BOLERO_CDC_TX1_TX_PATH_CTL, + TX_MACRO_DEC1, 0, + &tx_dec1_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC2 MUX", BOLERO_CDC_TX2_TX_PATH_CTL, + TX_MACRO_DEC2, 0, + &tx_dec2_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC3 MUX", BOLERO_CDC_TX3_TX_PATH_CTL, + TX_MACRO_DEC3, 0, + &tx_dec3_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC4 MUX", BOLERO_CDC_TX4_TX_PATH_CTL, + TX_MACRO_DEC4, 0, + &tx_dec4_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC5 MUX", BOLERO_CDC_TX5_TX_PATH_CTL, + TX_MACRO_DEC5, 0, + &tx_dec5_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC6 MUX", BOLERO_CDC_TX6_TX_PATH_CTL, + TX_MACRO_DEC6, 0, + &tx_dec6_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC7 MUX", BOLERO_CDC_TX7_TX_PATH_CTL, + TX_MACRO_DEC7, 0, + &tx_dec7_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("TX_MCLK", 0, SND_SOC_NOPM, 0, 0, + tx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route tx_audio_map[] = { + {"TX_AIF1 CAP", NULL, "TX_MCLK"}, + {"TX_AIF2 CAP", NULL, "TX_MCLK"}, + + {"TX_AIF1 CAP", NULL, "TX_AIF1_CAP Mixer"}, + {"TX_AIF2 CAP", NULL, "TX_AIF2_CAP Mixer"}, + + {"TX_AIF1_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX_AIF2_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX DEC0 MUX", "MSM_DMIC", "TX DMIC MUX0"}, + {"TX DMIC MUX0", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX0", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX0", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX0", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX0", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX0", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX0", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX0", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX0", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX0", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX0", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX0", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX0", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX0", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX0", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX1", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX1", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX1", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX1", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX1", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX1", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX1", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX1", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX1", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX2", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX2", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX2", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX2", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX2", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX2", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX2", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX2", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX2", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX3", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX3", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX3", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX3", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX3", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX3", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX3", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX3", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX3", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX4", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX4", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX4", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX4", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX4", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX4", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX4", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX4", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX4", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX5", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX5", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX5", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX5", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX5", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX5", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX5", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX5", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX5", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX6", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX6", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX6", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX6", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX6", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX6", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX6", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX6", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX6", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX7", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX7", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX7", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX7", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX7", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX7", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX7", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX7", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX7", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX7", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, +}; + +static const struct snd_kcontrol_new tx_macro_snd_controls[] = { + SOC_SINGLE_SX_TLV("TX_DEC0 Volume", + BOLERO_CDC_TX0_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC1 Volume", + BOLERO_CDC_TX1_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC2 Volume", + BOLERO_CDC_TX2_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC3 Volume", + BOLERO_CDC_TX3_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC4 Volume", + BOLERO_CDC_TX4_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC5 Volume", + BOLERO_CDC_TX5_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC6 Volume", + BOLERO_CDC_TX6_TX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("TX_DEC7 Volume", + BOLERO_CDC_TX7_TX_VOL_CTL, + 0, -84, 40, digital_gain), +}; + +static int tx_macro_swrm_clock(void *handle, bool enable) +{ + struct tx_macro_priv *tx_priv = (struct tx_macro_priv *) handle; + struct regmap *regmap = dev_get_regmap(tx_priv->dev->parent, NULL); + int ret = 0; + + mutex_lock(&tx_priv->swr_clk_lock); + + dev_dbg(tx_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + if (tx_priv->swr_clk_users == 0) { + ret = tx_macro_mclk_enable(tx_priv, 1); + if (ret < 0) { + dev_err(tx_priv->dev, + "%s: request clock enable failed\n", + __func__); + goto exit; + } + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x1C, 0x0C); + msm_cdc_pinctrl_select_active_state( + tx_priv->tx_swr_gpio_p); + } + tx_priv->swr_clk_users++; + } else { + if (tx_priv->swr_clk_users <= 0) { + dev_err(tx_priv->dev, + "tx swrm clock users already 0\n"); + tx_priv->swr_clk_users = 0; + goto exit; + } + tx_priv->swr_clk_users--; + if (tx_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_cdc_pinctrl_select_sleep_state( + tx_priv->tx_swr_gpio_p); + tx_macro_mclk_enable(tx_priv, 0); + } + } + dev_dbg(tx_priv->dev, "%s: swrm clock users %d\n", + __func__, tx_priv->swr_clk_users); +exit: + mutex_unlock(&tx_priv->swr_clk_lock); + return ret; +} + +static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, + struct tx_macro_priv *tx_priv) +{ + u32 div_factor = TX_MACRO_CLK_DIV_2; + u32 mclk_rate = TX_MACRO_MCLK_FREQ; + + if (dmic_sample_rate == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_2; + break; + case 3: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_3; + break; + case 4: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_4; + break; + case 6: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_6; + break; + case 8: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_8; + break; + case 16: + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_16; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + /* Valid dmic DIV factors */ + dev_dbg(tx_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + + return dmic_sample_rate; + +undefined_rate: + dev_dbg(tx_priv->dev, "%s: Invalid rate %d, for mclk %d\n", + __func__, dmic_sample_rate, mclk_rate); + dmic_sample_rate = TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +static int tx_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0, i = 0; + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + tx_dev = bolero_get_device_ptr(codec->dev, TX_MACRO); + if (!tx_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + tx_priv = dev_get_drvdata(tx_dev); + if (!tx_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + ret = snd_soc_dapm_new_controls(dapm, tx_macro_dapm_widgets, + ARRAY_SIZE(tx_macro_dapm_widgets)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add controls\n", __func__); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, tx_audio_map, + ARRAY_SIZE(tx_audio_map)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add routes\n", __func__); + return ret; + } + + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add widgets\n", __func__); + return ret; + } + + ret = snd_soc_add_codec_controls(codec, tx_macro_snd_controls, + ARRAY_SIZE(tx_macro_snd_controls)); + if (ret < 0) { + dev_err(tx_dev, "%s: Failed to add snd_ctls\n", __func__); + return ret; + } + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_priv->tx_hpf_work[i].tx_priv = tx_priv; + tx_priv->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tx_priv->tx_hpf_work[i].dwork, + tx_macro_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_priv->tx_mute_dwork[i].tx_priv = tx_priv; + tx_priv->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tx_priv->tx_mute_dwork[i].dwork, + tx_macro_mute_update_callback); + } + tx_priv->codec = codec; + + return 0; +} + +static int tx_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *tx_dev = NULL; + struct tx_macro_priv *tx_priv = NULL; + + if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__)) + return -EINVAL; + + tx_priv->codec = NULL; + return 0; +} + +static void tx_macro_add_child_devices(struct work_struct *work) +{ + struct tx_macro_priv *tx_priv = NULL; + struct platform_device *pdev = NULL; + struct device_node *node = NULL; + struct tx_macro_swr_ctrl_data *swr_ctrl_data = NULL, *temp = NULL; + int ret = 0; + u16 count = 0, ctrl_num = 0; + struct tx_macro_swr_ctrl_platform_data *platdata = NULL; + char plat_dev_name[TX_MACRO_SWR_STRING_LEN] = ""; + bool tx_swr_master_node = false; + + tx_priv = container_of(work, struct tx_macro_priv, + tx_macro_add_child_devices_work); + if (!tx_priv) { + pr_err("%s: Memory for tx_priv does not exist\n", + __func__); + return; + } + + if (!tx_priv->dev) { + pr_err("%s: tx dev does not exist\n", __func__); + return; + } + + if (!tx_priv->dev->of_node) { + dev_err(tx_priv->dev, + "%s: DT node for tx_priv does not exist\n", __func__); + return; + } + + platdata = &tx_priv->swr_plat_data; + tx_priv->child_count = 0; + + for_each_available_child_of_node(tx_priv->dev->of_node, node) { + tx_swr_master_node = false; + if (strnstr(node->name, "tx_swr_master", + strlen("tx_swr_master")) != NULL) + tx_swr_master_node = true; + + if (tx_swr_master_node) + strlcpy(plat_dev_name, "tx_swr_ctrl", + (TX_MACRO_SWR_STRING_LEN - 1)); + else + strlcpy(plat_dev_name, node->name, + (TX_MACRO_SWR_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(tx_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = tx_priv->dev; + pdev->dev.of_node = node; + + if (tx_swr_master_node) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (tx_swr_master_node) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tx_macro_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto fail_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].tx_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tx_priv->swr_ctrl_data = swr_ctrl_data; + } + if (tx_priv->child_count < TX_MACRO_CHILD_DEVICES_MAX) + tx_priv->pdev_child_devices[ + tx_priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < tx_priv->child_count; count++) + platform_device_put(tx_priv->pdev_child_devices[count]); +err: + return; +} + +static void tx_macro_init_ops(struct macro_ops *ops, + char __iomem *tx_io_base) +{ + memset(ops, 0, sizeof(struct macro_ops)); + ops->init = tx_macro_init; + ops->exit = tx_macro_deinit; + ops->io_base = tx_io_base; + ops->dai_ptr = tx_macro_dai; + ops->num_dais = ARRAY_SIZE(tx_macro_dai); + ops->mclk_fn = tx_macro_mclk_ctrl; +} + +static int tx_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops = {0}; + struct tx_macro_priv *tx_priv = NULL; + u32 tx_base_addr = 0, sample_rate = 0; + char __iomem *tx_io_base = NULL; + struct clk *tx_core_clk = NULL, *tx_npl_clk = NULL; + int ret = 0; + const char *dmic_sample_rate = "qcom,tx-dmic-sample-rate"; + + tx_priv = devm_kzalloc(&pdev->dev, sizeof(struct tx_macro_priv), + GFP_KERNEL); + if (!tx_priv) + return -ENOMEM; + platform_set_drvdata(pdev, tx_priv); + + tx_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &tx_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + dev_set_drvdata(&pdev->dev, tx_priv); + tx_priv->tx_swr_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,tx-swr-gpios", 0); + if (!tx_priv->tx_swr_gpio_p) { + dev_err(&pdev->dev, "%s: swr_gpios handle not provided!\n", + __func__); + return -EINVAL; + } + tx_io_base = devm_ioremap(&pdev->dev, + tx_base_addr, TX_MACRO_MAX_OFFSET); + if (!tx_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -ENOMEM; + } + tx_priv->tx_io_base = tx_io_base; + ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate, + &sample_rate); + if (ret) { + dev_err(&pdev->dev, + "%s: could not find sample_rate entry in dt\n", + __func__); + tx_priv->dmic_clk_div = TX_MACRO_CLK_DIV_2; + } else { + if (tx_macro_validate_dmic_sample_rate( + sample_rate, tx_priv) == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED) + return -EINVAL; + } + + INIT_WORK(&tx_priv->tx_macro_add_child_devices_work, + tx_macro_add_child_devices); + tx_priv->swr_plat_data.handle = (void *) tx_priv; + tx_priv->swr_plat_data.read = NULL; + tx_priv->swr_plat_data.write = NULL; + tx_priv->swr_plat_data.bulk_write = NULL; + tx_priv->swr_plat_data.clk = tx_macro_swrm_clock; + tx_priv->swr_plat_data.handle_irq = NULL; + /* Register MCLK for tx macro */ + tx_core_clk = devm_clk_get(&pdev->dev, "tx_core_clk"); + if (IS_ERR(tx_core_clk)) { + ret = PTR_ERR(tx_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "tx_core_clk", ret); + return ret; + } + tx_priv->tx_core_clk = tx_core_clk; + /* Register npl clk for soundwire */ + tx_npl_clk = devm_clk_get(&pdev->dev, "tx_npl_clk"); + if (IS_ERR(tx_npl_clk)) { + ret = PTR_ERR(tx_npl_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "tx_npl_clk", ret); + return ret; + } + tx_priv->tx_npl_clk = tx_npl_clk; + + mutex_init(&tx_priv->mclk_lock); + mutex_init(&tx_priv->swr_clk_lock); + tx_macro_init_ops(&ops, tx_io_base); + ret = bolero_register_macro(&pdev->dev, TX_MACRO, &ops); + if (ret) { + dev_err(&pdev->dev, + "%s: register macro failed\n", __func__); + goto err_reg_macro; + } + schedule_work(&tx_priv->tx_macro_add_child_devices_work); + return 0; +err_reg_macro: + mutex_destroy(&tx_priv->mclk_lock); + mutex_destroy(&tx_priv->swr_clk_lock); + return ret; +} + +static int tx_macro_remove(struct platform_device *pdev) +{ + struct tx_macro_priv *tx_priv = NULL; + u16 count = 0; + + tx_priv = platform_get_drvdata(pdev); + + if (!tx_priv) + return -EINVAL; + + kfree(tx_priv->swr_ctrl_data); + for (count = 0; count < tx_priv->child_count && + count < TX_MACRO_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tx_priv->pdev_child_devices[count]); + + mutex_destroy(&tx_priv->mclk_lock); + mutex_destroy(&tx_priv->swr_clk_lock); + bolero_unregister_macro(&pdev->dev, TX_MACRO); + return 0; +} + + +static const struct of_device_id tx_macro_dt_match[] = { + {.compatible = "qcom,tx-macro"}, + {} +}; + +static struct platform_driver tx_macro_driver = { + .driver = { + .name = "tx_macro", + .owner = THIS_MODULE, + .of_match_table = tx_macro_dt_match, + }, + .probe = tx_macro_probe, + .remove = tx_macro_remove, +}; + +module_platform_driver(tx_macro_driver); + +MODULE_DESCRIPTION("TX macro driver"); +MODULE_LICENSE("GPL v2"); From a7ecc58a35c3155ef1bc2cc66b4dfb4fdecf895c Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Fri, 15 Jun 2018 16:55:02 +0530 Subject: [PATCH 2/2] asoc: bolero: Add support for RX macro driver Add support for RX digital portion to register as macro to bolero codec. ASoC dapm and dai functionality of RX macro is associated to bolero codec itself. Change-Id: Idb924b33cfc5d258d2f876a31603e724ad9699e9 Signed-off-by: Laxminath Kasam --- asoc/codecs/bolero/bolero-cdc-regmap.c | 27 + asoc/codecs/bolero/rx-macro.c | 2560 ++++++++++++++++++++++++ 2 files changed, 2587 insertions(+) create mode 100644 asoc/codecs/bolero/rx-macro.c diff --git a/asoc/codecs/bolero/bolero-cdc-regmap.c b/asoc/codecs/bolero/bolero-cdc-regmap.c index ef5b5908591f..ecc13243d999 100644 --- a/asoc/codecs/bolero/bolero-cdc-regmap.c +++ b/asoc/codecs/bolero/bolero-cdc-regmap.c @@ -816,6 +816,33 @@ static bool bolero_is_volatile_register(struct device *dev, case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: case BOLERO_CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + case BOLERO_CDC_RX_TOP_HPHL_COMP_RD_LSB: + case BOLERO_CDC_RX_TOP_HPHL_COMP_RD_MSB: + case BOLERO_CDC_RX_TOP_HPHR_COMP_RD_LSB: + case BOLERO_CDC_RX_TOP_HPHR_COMP_RD_MSB: + case BOLERO_CDC_RX_TOP_DSD0_DEBUG_CFG2: + case BOLERO_CDC_RX_TOP_DSD1_DEBUG_CFG2: + case BOLERO_CDC_RX_BCL_VBAT_GAIN_MON_VAL: + case BOLERO_CDC_RX_BCL_VBAT_DECODE_ST: + case BOLERO_CDC_RX_INTR_CTRL_PIN1_STATUS0: + case BOLERO_CDC_RX_INTR_CTRL_PIN2_STATUS0: + case BOLERO_CDC_RX_COMPANDER0_CTL6: + case BOLERO_CDC_RX_COMPANDER1_CTL6: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC0_STATUS_FIFO: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC1_STATUS_FIFO: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB: + case BOLERO_CDC_RX_EC_ASRC2_STATUS_FIFO: return true; } return false; diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c new file mode 100644 index 000000000000..d5071279f53f --- /dev/null +++ b/asoc/codecs/bolero/rx-macro.c @@ -0,0 +1,2560 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bolero-cdc.h" +#include "bolero-cdc-registers.h" +#include "../msm-cdc-pinctrl.h" + +#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define RX_MACRO_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define RX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define RX_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define RX_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define RX_MACRO_MAX_OFFSET 0x1000 + +#define RX_MACRO_MAX_DMA_CH_PER_PORT 2 +#define RX_SWR_STRING_LEN 80 +#define RX_MACRO_CHILD_DEVICES_MAX 3 + +#define RX_MACRO_INTERP_MUX_NUM_INPUTS 3 +#define RX_MACRO_SIDETONE_IIR_COEFF_MAX 5 + +#define STRING(name) #name +#define RX_MACRO_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define RX_MACRO_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define RX_MACRO_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +#define RX_MACRO_RX_PATH_OFFSET 0x80 +#define RX_MACRO_COMP_OFFSET 0x40 + +enum { + INTERP_HPHL, + INTERP_HPHR, + INTERP_AUX, + INTERP_MAX +}; + +enum { + RX_MACRO_RX0, + RX_MACRO_RX1, + RX_MACRO_RX2, + RX_MACRO_RX3, + RX_MACRO_RX4, + RX_MACRO_RX5, + RX_MACRO_PORTS_MAX +}; + +enum { + RX_MACRO_COMP1, /* HPH_L */ + RX_MACRO_COMP2, /* HPH_R */ + RX_MACRO_COMP_MAX +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +struct rx_macro_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +static int rx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int rx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot); +static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int rx_macro_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx); + +/* Hold instance to soundwire platform device */ +struct rx_swr_ctrl_data { + struct platform_device *rx_swr_pdev; +}; + +struct rx_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +enum { + RX_MACRO_AIF1_PB = 0, + RX_MACRO_AIF2_PB, + RX_MACRO_AIF3_PB, + RX_MACRO_AIF4_PB, + RX_MACRO_MAX_DAIS, +}; + +enum { + RX_MACRO_AIF1_CAP = 0, + RX_MACRO_AIF2_CAP, + RX_MACRO_AIF3_CAP, + RX_MACRO_MAX_AIF_CAP_DAIS +}; +/* + * @dev: rx macro device pointer + * @comp_enabled: compander enable mixer value set + * @prim_int_users: Users of interpolator + * @rx_mclk_users: RX MCLK users count + * @vi_feed_value: VI sense mask + * @swr_clk_lock: to lock swr master clock operations + * @swr_ctrl_data: SoundWire data structure + * @swr_plat_data: Soundwire platform data + * @rx_macro_add_child_devices_work: work for adding child devices + * @rx_swr_gpio_p: used by pinctrl API + * @rx_core_clk: MCLK for rx macro + * @rx_npl_clk: NPL clock for RX soundwire + * @codec: codec handle + */ +struct rx_macro_priv { + struct device *dev; + int comp_enabled[RX_MACRO_COMP_MAX]; + /* Main path clock users count */ + int main_clk_users[INTERP_MAX]; + int rx_port_value[RX_MACRO_PORTS_MAX]; + u16 prim_int_users[INTERP_MAX]; + int rx_mclk_users; + int swr_clk_users; + int rx_mclk_cnt; + struct mutex mclk_lock; + struct mutex swr_clk_lock; + struct rx_swr_ctrl_data *swr_ctrl_data; + struct rx_swr_ctrl_platform_data swr_plat_data; + struct work_struct rx_macro_add_child_devices_work; + struct device_node *rx_swr_gpio_p; + struct clk *rx_core_clk; + struct clk *rx_npl_clk; + struct snd_soc_codec *codec; + unsigned long active_ch_mask[RX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS]; + u16 bit_width[RX_MACRO_MAX_DAIS]; + char __iomem *rx_io_base; + char __iomem *rx_mclk_mode_muxsel; + struct rx_macro_idle_detect_config idle_det_cfg; + u8 sidetone_coeff_array[IIR_MAX][BAND_MAX] + [RX_MACRO_SIDETONE_IIR_COEFF_MAX * 4]; + + struct platform_device *pdev_child_devices + [RX_MACRO_CHILD_DEVICES_MAX]; + int child_count; +}; + +static struct snd_soc_dai_driver rx_macro_dai[]; +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char *const rx_macro_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +RX_MACRO_DAPM_ENUM(rx_int0_2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int_mix_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_2, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); + + +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int0_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp0, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp1, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_mix_inp2, BOLERO_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); + +RX_MACRO_DAPM_ENUM(rx_int0_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +RX_MACRO_DAPM_ENUM(rx_int1_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +RX_MACRO_DAPM_ENUM(rx_int2_mix2_inp, BOLERO_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); + +RX_MACRO_DAPM_ENUM(rx_mix_tx0, BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 4, + rx_echo_mux_text); +RX_MACRO_DAPM_ENUM(rx_mix_tx1, BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); +RX_MACRO_DAPM_ENUM(rx_mix_tx2, BOLERO_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); + +RX_MACRO_DAPM_ENUM(iir0_inp0, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp1, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp2, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir0_inp3, BOLERO_CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp0, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp1, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp2, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +RX_MACRO_DAPM_ENUM(iir1_inp3, BOLERO_CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +RX_MACRO_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, + rx_int0_1_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, + rx_int1_1_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, + rx_int2_1_interp_mux_text); + +RX_MACRO_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, + rx_int0_2_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, + rx_int1_2_interp_mux_text); +RX_MACRO_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, + rx_int2_2_interp_mux_text); + +RX_MACRO_DAPM_ENUM_EXT(rx_int0_dem_inp, BOLERO_CDC_RX_RX0_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + rx_macro_int_dem_inp_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_int1_dem_inp, BOLERO_CDC_RX_RX1_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + rx_macro_int_dem_inp_mux_put); + +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx0, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx1, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx2, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx3, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx4, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); +RX_MACRO_DAPM_ENUM_EXT(rx_macro_rx5, SND_SOC_NOPM, 0, rx_macro_mux_text, + rx_macro_mux_get, rx_macro_mux_put); + +static struct snd_soc_dai_ops rx_macro_dai_ops = { + .hw_params = rx_macro_hw_params, + .get_channel_map = rx_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver rx_macro_dai[] = { + { + .name = "rx_macro_rx1", + .id = RX_MACRO_AIF1_PB, + .playback = { + .stream_name = "RX_MACRO_AIF1 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx2", + .id = RX_MACRO_AIF2_PB, + .playback = { + .stream_name = "RX_MACRO_AIF2 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx3", + .id = RX_MACRO_AIF3_PB, + .playback = { + .stream_name = "RX_MACRO_AIF3 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx4", + .id = RX_MACRO_AIF4_PB, + .playback = { + .stream_name = "RX_MACRO_AIF4 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, +}; + +static bool rx_macro_get_data(struct snd_soc_codec *codec, + struct device **rx_dev, + struct rx_macro_priv **rx_priv, + const char *func_name) +{ + *rx_dev = bolero_get_device_ptr(codec->dev, RX_MACRO); + + if (!(*rx_dev)) { + dev_err(codec->dev, + "%s: null device for macro!\n", func_name); + return false; + } + + *rx_priv = dev_get_drvdata((*rx_dev)); + if (!(*rx_priv)) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", func_name); + return false; + } + + if (!(*rx_priv)->codec) { + dev_err(codec->dev, + "%s: tx_priv codec is not initialized!\n", func_name); + return false; + } + + return true; +} + +static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = 0; + unsigned short look_ahead_dly_reg = + BOLERO_CDC_RX_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == BOLERO_CDC_RX_RX0_RX_PATH_CFG1) + look_ahead_dly_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0; + else if (e->reg == BOLERO_CDC_RX_RX1_RX_PATH_CFG1) + look_ahead_dly_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp = 0; + u32 j = 0, port = 0; + u16 int_mux_cfg0 = 0, int_mux_cfg1 = 0; + u16 int_fs_reg = 0; + u8 int_mux_cfg0_val = 0, int_mux_cfg1_val = 0; + u8 inp0_sel = 0, inp1_sel = 0, inp2_sel = 0; + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + for_each_set_bit(port, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < RX_MACRO_RX0) || + (int_1_mix1_inp > RX_MACRO_PORTS_MAX)) { + pr_err("%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the rx port + * is connected + */ + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1 = int_mux_cfg0 + 4; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x07; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x038; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x038; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + + 0x80 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, rate_reg_val); + } + int_mux_cfg0 += 8; + } + } + + return 0; +} + +static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp = 0; + u32 j = 0, port = 0; + u16 int_mux_cfg1 = 0, int_fs_reg = 0; + u8 int_mux_cfg1_val = 0; + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + for_each_set_bit(port, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + int_2_inp = port; + if ((int_2_inp < RX_MACRO_RX0) || + (int_2_inp > RX_MACRO_PORTS_MAX)) { + pr_err("%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x07; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + + 0x80 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, rate_reg_val); + } + int_mux_cfg1 += 8; + } + } + return 0; +} + +static int rx_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i = 0, ret = 0; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = rx_macro_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = rx_macro_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int rx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = rx_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + pr_err("%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + rx_priv->bit_width[dai->id] = params_width(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + default: + break; + } + return 0; +} + +static int rx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_codec *codec = dai->codec; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + unsigned int temp = 0, ch_mask = 0; + u16 i = 0; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + switch (dai->id) { + case RX_MACRO_AIF1_PB: + case RX_MACRO_AIF2_PB: + case RX_MACRO_AIF3_PB: + case RX_MACRO_AIF4_PB: + for_each_set_bit(temp, &rx_priv->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + ch_mask |= (1 << i); + if (++i == RX_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + *rx_slot = ch_mask; + *rx_num = rx_priv->active_ch_cnt[dai->id]; + break; + default: + dev_err(rx_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int rx_macro_mclk_enable(struct rx_macro_priv *rx_priv, + bool mclk_enable, bool dapm) +{ + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + int ret = 0; + + dev_dbg(rx_priv->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, rx_priv->rx_mclk_users); + + mutex_lock(&rx_priv->mclk_lock); + if (mclk_enable) { + if (rx_priv->rx_mclk_users == 0) { + ret = bolero_request_clock(rx_priv->dev, + RX_MACRO, MCLK_MUX0, true); + if (ret < 0) { + dev_err(rx_priv->dev, + "%s: rx request clock enable failed\n", + __func__); + goto exit; + } + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, + RX_START_OFFSET, + RX_MAX_OFFSET); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + } + rx_priv->rx_mclk_users++; + } else { + if (rx_priv->rx_mclk_users <= 0) { + dev_err(rx_priv->dev, "%s: clock already disabled\n", + __func__); + rx_priv->rx_mclk_users = 0; + goto exit; + } + rx_priv->rx_mclk_users--; + if (rx_priv->rx_mclk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + bolero_request_clock(rx_priv->dev, + RX_MACRO, MCLK_MUX0, false); + } + } +exit: + mutex_unlock(&rx_priv->mclk_lock); + return ret; +} + +static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(rx_dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = rx_macro_mclk_enable(rx_priv, 1, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = rx_macro_mclk_enable(rx_priv, 0, true); + break; + default: + dev_err(rx_priv->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int rx_macro_mclk_ctrl(struct device *dev, bool enable) +{ + struct rx_macro_priv *rx_priv = dev_get_drvdata(dev); + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(rx_priv->rx_core_clk); + if (ret < 0) { + dev_err(dev, "%s:rx mclk enable failed\n", __func__); + return ret; + } + ret = clk_prepare_enable(rx_priv->rx_npl_clk); + if (ret < 0) { + clk_disable_unprepare(rx_priv->rx_core_clk); + dev_err(dev, "%s:rx npl_clk enable failed\n", + __func__); + return ret; + } + if (rx_priv->rx_mclk_cnt++ == 0) + iowrite32(0x1, rx_priv->rx_mclk_mode_muxsel); + } else { + if (rx_priv->rx_mclk_cnt <= 0) { + dev_dbg(dev, "%s:rx mclk already disabled\n", __func__); + rx_priv->rx_mclk_cnt = 0; + return 0; + } + if (--rx_priv->rx_mclk_cnt == 0) + iowrite32(0x0, rx_priv->rx_mclk_mode_muxsel); + clk_disable_unprepare(rx_priv->rx_npl_clk); + clk_disable_unprepare(rx_priv->rx_core_clk); + } + + return 0; +} + +static int rx_macro_find_playback_dai_id_for_port(int port_id, + struct rx_macro_priv *rx_priv) +{ + int i = 0; + + for (i = RX_MACRO_AIF1_PB; i < RX_MACRO_MAX_DAIS; i++) { + if (test_bit(port_id, &rx_priv->active_ch_mask[i])) + return i; + } + + return -EINVAL; +} + +static int rx_macro_set_idle_detect_thr(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr = NULL, num_ports = NULL; + int bit_width = 0, i = 0; + int mux_reg = 0, mux_reg_val = 0; + int dai_id = 0, idle_thr = 0; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!rx_priv->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which cdc_dma port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1 + + 2 * interp; + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val <= INTn_2_INP_SEL_RX5)) { + *port_ptr++ = mux_reg_val - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = RX_MACRO_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX5)) { + *port_ptr++ = mux_reg_val - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = rx_macro_find_playback_dai_id_for_port(port_id[i++], + rx_priv); + + if ((dai_id >= 0) && (dai_id < RX_MACRO_MAX_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + rx_priv->bit_width[dai_id]); + + if (rx_priv->bit_width[dai_id] > bit_width) + bit_width = rx_priv->bit_width[dai_id]; + } + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, rx_priv->idle_det_cfg.hph_idle_thr); + + if ((rx_priv->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < rx_priv->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, BOLERO_CDC_RX_IDLE_DETECT_CFG3, idle_thr); + rx_priv->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0, mix_reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + if (w->shift >= INTERP_MAX) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + } + + gain_reg = BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL + + (w->shift * RX_MACRO_RX_PATH_OFFSET); + mix_reg = BOLERO_CDC_RX_RX0_RX_PATH_MIX_CTL + + (w->shift * RX_MACRO_RX_PATH_OFFSET); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_set_idle_detect_thr(codec, rx_priv, w->shift, + INTERP_MIX_PATH); + rx_macro_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, gain_reg, + snd_soc_read(codec, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + rx_macro_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + break; + } + + return 0; +} + +static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 gain_reg = 0; + u16 reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= INTERP_MAX) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + } + + reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + (w->shift * + RX_MACRO_RX_PATH_OFFSET); + gain_reg = BOLERO_CDC_RX_RX0_RX_VOL_CTL + (w->shift * + RX_MACRO_RX_PATH_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_set_idle_detect_thr(codec, rx_priv, w->shift, + INTERP_MAIN_PATH); + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, gain_reg, + snd_soc_read(codec, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + } + + return 0; +} + +static int rx_macro_config_compander(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp_n, int event) +{ + int comp = 0; + u16 comp_ctl0_reg = 0, rx_path_cfg0_reg = 0; + + /* AUX does not have compander */ + if (interp_n == INTERP_AUX) + return 0; + + comp = interp_n; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, rx_priv->comp_enabled[comp]); + + if (!rx_priv->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = BOLERO_CDC_RX_COMPANDER0_CTL0 + + (comp * RX_MACRO_COMP_OFFSET); + rx_path_cfg0_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG0 + + (comp * RX_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void rx_macro_hd2_control(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg = 0; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = BOLERO_CDC_RX_RX1_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = BOLERO_CDC_RX_RX2_RX_PATH_SEC3; + hd2_enable_reg = BOLERO_CDC_RX_RX2_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int rx_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = rx_priv->comp_enabled[comp]; + return 0; +} + +static int rx_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, rx_priv->comp_enabled[comp], value); + rx_priv->comp_enabled[comp] = value; + + return 0; +} + +static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + ucontrol->value.integer.value[0] = + rx_priv->rx_port_value[widget->shift]; + return 0; +} + +static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 aif_rst = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + aif_rst = rx_priv->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) { + dev_err(rx_dev, "%s:AIF reset already\n", __func__); + return 0; + } + } + rx_priv->rx_port_value[widget->shift] = rx_port_value; + + switch (rx_port_value) { + case 0: + clear_bit(widget->shift, + &rx_priv->active_ch_mask[aif_rst - 1]); + rx_priv->active_ch_cnt[aif_rst - 1]--; + break; + case 1: + case 2: + case 3: + case 4: + set_bit(widget->shift, + &rx_priv->active_ch_mask[rx_port_value - 1]); + rx_priv->active_ch_cnt[rx_port_value - 1]++; + break; + default: + dev_err(codec->dev, + "%s:Invalid AIF_ID for RX_MACRO MUX\n", __func__); + goto err; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +err: + return -EINVAL; +} + +static void rx_macro_idle_detect_control(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + int interp, int event) +{ + int reg = 0, mask = 0, val = 0; + + if (!rx_priv->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = BOLERO_CDC_RX_IDLE_DETECT_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + rx_priv->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, BOLERO_CDC_RX_IDLE_DETECT_CFG3, 0x0); + } +} + +static void rx_macro_hphdelay_lutbypass(struct snd_soc_codec *codec, + struct rx_macro_priv *rx_priv, + u16 interp_idx, int event) +{ + u8 hph_dly_mask = 0; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER0_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = BOLERO_CDC_RX_COMPANDER1_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, BOLERO_CDC_RX_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + +static int rx_macro_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + u16 main_reg = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + main_reg = BOLERO_CDC_RX_RX0_RX_PATH_CTL + + (interp_idx * RX_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (rx_priv->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + rx_macro_idle_detect_control(codec, rx_priv, + interp_idx, event); + rx_macro_hd2_control(codec, interp_idx, event); + rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx, + event); + rx_macro_config_compander(codec, rx_priv, + interp_idx, event); + } + rx_priv->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + rx_priv->main_clk_users[interp_idx]--; + if (rx_priv->main_clk_users[interp_idx] <= 0) { + rx_priv->main_clk_users[interp_idx] = 0; + rx_macro_config_compander(codec, rx_priv, + interp_idx, event); + rx_macro_hphdelay_lutbypass(codec, rx_priv, interp_idx, + event); + rx_macro_hd2_control(codec, interp_idx, event); + rx_macro_idle_detect_control(codec, rx_priv, + interp_idx, event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, rx_priv->main_clk_users[interp_idx]); + + return rx_priv->main_clk_users[interp_idx]; +} + +static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg = 0; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = BOLERO_CDC_RX_RX0_RX_PATH_CFG1 + + RX_MACRO_RX_PATH_OFFSET * (w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + rx_macro_enable_interp_clk(codec, event, w->shift); + break; + default: + break; + }; + return 0; +} + +static void rx_macro_restore_iir_coeff(struct rx_macro_priv *rx_priv, int iir_idx, + int band_idx) +{ + u16 reg_add = 0, coeff_idx = 0, idx = 0; + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + + regmap_write(regmap, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + reg_add = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx; + + /* 5 coefficients per band and 4 writes per coefficient */ + for (coeff_idx = 0; coeff_idx < RX_MACRO_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + /* Four 8 bit values(one 32 bit) per coefficient */ + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + regmap_write(regmap, reg_add, + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++]); + } +} + +static int rx_macro_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 0x80 */ + u16 iir_reg = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL + 0x80 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int rx_macro_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status = 0; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = BOLERO_CDC_RX_SIDETONE_IIR0_IIR_CTL + 0x80 * iir_idx; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_macro_restore_iir_coeff(rx_priv, iir_idx, band_idx); + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx)); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 0x80 * iir_idx)) << 8); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 0x80 * iir_idx)) << 16); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int rx_macro_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx), + (value >> 24) & 0x3F); +} + +static int rx_macro_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx, idx = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (BOLERO_CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + /* Store the coefficients in sidetone coeff array */ + for (coeff_idx = 0; coeff_idx < RX_MACRO_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + uint32_t value = ucontrol->value.integer.value[coeff_idx]; + + set_iir_band_coeff(codec, iir_idx, band_idx, value); + + /* Four 8 bit values(one 32 bit) per coefficient */ + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value & 0xFF); + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 8) & 0xFF; + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 16) & 0xFF; + rx_priv->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 24) & 0xFF; + } + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int rx_macro_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static const struct snd_kcontrol_new rx_macro_snd_controls[] = { + SOC_SINGLE_SX_TLV("RX_RX0 Digital Volume", + BOLERO_CDC_RX_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX1 Digital Volume", + BOLERO_CDC_RX_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX2 Digital Volume", + BOLERO_CDC_RX_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX0 Mix Digital Volume", + BOLERO_CDC_RX_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX1 Mix Digital Volume", + BOLERO_CDC_RX_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX_RX2 Mix Digital Volume", + BOLERO_CDC_RX_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + + SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + BOLERO_CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + BOLERO_CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + rx_macro_iir_enable_audio_mixer_get, + rx_macro_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + rx_macro_iir_band_audio_mixer_get, + rx_macro_iir_band_audio_mixer_put), +}; + +static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF2 PB", "RX_MACRO_AIF2 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF3 PB", "RX_MACRO_AIF3 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF4 PB", "RX_MACRO_AIF4 Playback", 0, + SND_SOC_NOPM, 0, 0), + + RX_MACRO_DAPM_MUX("RX_MACRO RX0 MUX", RX_MACRO_RX0, rx_macro_rx0), + RX_MACRO_DAPM_MUX("RX_MACRO RX1 MUX", RX_MACRO_RX1, rx_macro_rx1), + RX_MACRO_DAPM_MUX("RX_MACRO RX2 MUX", RX_MACRO_RX2, rx_macro_rx2), + RX_MACRO_DAPM_MUX("RX_MACRO RX3 MUX", RX_MACRO_RX3, rx_macro_rx3), + RX_MACRO_DAPM_MUX("RX_MACRO RX4 MUX", RX_MACRO_RX4, rx_macro_rx4), + RX_MACRO_DAPM_MUX("RX_MACRO RX5 MUX", RX_MACRO_RX5, rx_macro_rx5), + + SND_SOC_DAPM_MIXER("RX_RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + + RX_MACRO_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + RX_MACRO_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + RX_MACRO_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + RX_MACRO_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + RX_MACRO_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0), + RX_MACRO_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1), + RX_MACRO_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2), + RX_MACRO_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3), + + SND_SOC_DAPM_MIXER_E("IIR0", BOLERO_CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", BOLERO_CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", BOLERO_CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", BOLERO_CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + RX_MACRO_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0), + RX_MACRO_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1), + RX_MACRO_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2), + RX_MACRO_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + RX_MACRO_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1), + RX_MACRO_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + RX_MACRO_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + RX_MACRO_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + RX_MACRO_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int0_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int1_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_AUX, + 0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPHL_OUT"), + SND_SOC_DAPM_OUTPUT("HPHR_OUT"), + SND_SOC_DAPM_OUTPUT("AUX_OUT"), + + SND_SOC_DAPM_INPUT("RX_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC1_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC2_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC3_INP"), + + SND_SOC_DAPM_SUPPLY_S("RX_MCLK", 0, SND_SOC_NOPM, 0, 0, + rx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rx_audio_map[] = { + {"RX AIF1 PB", NULL, "RX_MCLK"}, + {"RX AIF2 PB", NULL, "RX_MCLK"}, + {"RX AIF3 PB", NULL, "RX_MCLK"}, + {"RX AIF4 PB", NULL, "RX_MCLK"}, + + {"RX_MACRO RX0 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX1 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX2 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX3 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX4 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX5 MUX", "AIF1_PB", "RX AIF1 PB"}, + + {"RX_MACRO RX0 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX1 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX2 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX3 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX4 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX5 MUX", "AIF2_PB", "RX AIF2 PB"}, + + {"RX_MACRO RX0 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX1 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX2 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX3 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX4 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX5 MUX", "AIF3_PB", "RX AIF3 PB"}, + + {"RX_MACRO RX0 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX1 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX2 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX3 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX4 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX5 MUX", "AIF4_PB", "RX AIF4 PB"}, + + {"RX_RX0", NULL, "RX_MACRO RX0 MUX"}, + {"RX_RX1", NULL, "RX_MACRO RX1 MUX"}, + {"RX_RX2", NULL, "RX_MACRO RX2 MUX"}, + {"RX_RX3", NULL, "RX_MACRO RX3 MUX"}, + {"RX_RX4", NULL, "RX_MACRO RX4 MUX"}, + {"RX_RX5", NULL, "RX_MACRO RX5 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT2_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "RX_RX0"}, + {"RX INT0_2 MUX", "RX1", "RX_RX1"}, + {"RX INT0_2 MUX", "RX2", "RX_RX2"}, + {"RX INT0_2 MUX", "RX3", "RX_RX3"}, + {"RX INT0_2 MUX", "RX4", "RX_RX4"}, + {"RX INT0_2 MUX", "RX5", "RX_RX5"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "RX_RX0"}, + {"RX INT1_2 MUX", "RX1", "RX_RX1"}, + {"RX INT1_2 MUX", "RX2", "RX_RX2"}, + {"RX INT1_2 MUX", "RX3", "RX_RX3"}, + {"RX INT1_2 MUX", "RX4", "RX_RX4"}, + {"RX INT1_2 MUX", "RX5", "RX_RX5"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "RX_RX0"}, + {"RX INT2_2 MUX", "RX1", "RX_RX1"}, + {"RX INT2_2 MUX", "RX2", "RX_RX2"}, + {"RX INT2_2 MUX", "RX3", "RX_RX3"}, + {"RX INT2_2 MUX", "RX4", "RX_RX4"}, + {"RX INT2_2 MUX", "RX5", "RX_RX5"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"HPHL_OUT", NULL, "RX INT0 DEM MUX"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"}, + {"HPHR_OUT", NULL, "RX INT1 DEM MUX"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"AUX_OUT", NULL, "RX INT2 MIX2"}, + + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP3 MUX", "RX5", "RX_RX5"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP3 MUX", "RX5", "RX_RX5"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int rx_swrm_clock(void *handle, bool enable) +{ + struct rx_macro_priv *rx_priv = (struct rx_macro_priv *) handle; + struct regmap *regmap = dev_get_regmap(rx_priv->dev->parent, NULL); + int ret = 0; + + mutex_lock(&rx_priv->swr_clk_lock); + + dev_dbg(rx_priv->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + if (rx_priv->swr_clk_users == 0) { + ret = rx_macro_mclk_enable(rx_priv, 1, true); + if (ret < 0) { + dev_err(rx_priv->dev, + "%s: rx request clock enable failed\n", + __func__); + goto exit; + } + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x1C, 0x0C); + msm_cdc_pinctrl_select_active_state( + rx_priv->rx_swr_gpio_p); + } + rx_priv->swr_clk_users++; + } else { + if (rx_priv->swr_clk_users <= 0) { + dev_err(rx_priv->dev, + "%s: rx swrm clock users already reset\n", + __func__); + rx_priv->swr_clk_users = 0; + goto exit; + } + rx_priv->swr_clk_users--; + if (rx_priv->swr_clk_users == 0) { + regmap_update_bits(regmap, + BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_cdc_pinctrl_select_sleep_state( + rx_priv->rx_swr_gpio_p); + rx_macro_mclk_enable(rx_priv, 0, true); + } + } + dev_dbg(rx_priv->dev, "%s: swrm clock users %d\n", + __func__, rx_priv->swr_clk_users); +exit: + mutex_unlock(&rx_priv->swr_clk_lock); + return ret; +} + +static int rx_macro_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret = 0; + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + rx_dev = bolero_get_device_ptr(codec->dev, RX_MACRO); + if (!rx_dev) { + dev_err(codec->dev, + "%s: null device for macro!\n", __func__); + return -EINVAL; + } + rx_priv = dev_get_drvdata(rx_dev); + if (!rx_priv) { + dev_err(codec->dev, + "%s: priv is null for macro!\n", __func__); + return -EINVAL; + } + + ret = snd_soc_dapm_new_controls(dapm, rx_macro_dapm_widgets, + ARRAY_SIZE(rx_macro_dapm_widgets)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add controls\n", __func__); + return ret; + } + ret = snd_soc_dapm_add_routes(dapm, rx_audio_map, + ARRAY_SIZE(rx_audio_map)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add routes\n", __func__); + return ret; + } + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add widgets\n", __func__); + return ret; + } + ret = snd_soc_add_codec_controls(codec, rx_macro_snd_controls, + ARRAY_SIZE(rx_macro_snd_controls)); + if (ret < 0) { + dev_err(rx_dev, "%s: failed to add snd_ctls\n", __func__); + return ret; + } + + rx_priv->codec = codec; + + return 0; +} + +static int rx_macro_deinit(struct snd_soc_codec *codec) +{ + struct device *rx_dev = NULL; + struct rx_macro_priv *rx_priv = NULL; + + if (!rx_macro_get_data(codec, &rx_dev, &rx_priv, __func__)) + return -EINVAL; + + rx_priv->codec = NULL; + + return 0; +} + +static void rx_macro_add_child_devices(struct work_struct *work) +{ + struct rx_macro_priv *rx_priv = NULL; + struct platform_device *pdev = NULL; + struct device_node *node = NULL; + struct rx_swr_ctrl_data *swr_ctrl_data = NULL, *temp = NULL; + int ret = 0; + u16 count = 0, ctrl_num = 0; + struct rx_swr_ctrl_platform_data *platdata = NULL; + char plat_dev_name[RX_SWR_STRING_LEN] = ""; + bool rx_swr_master_node = false; + + rx_priv = container_of(work, struct rx_macro_priv, + rx_macro_add_child_devices_work); + if (!rx_priv) { + pr_err("%s: Memory for rx_priv does not exist\n", + __func__); + return; + } + + if (!rx_priv->dev) { + pr_err("%s: RX device does not exist\n", __func__); + return; + } + + if(!rx_priv->dev->of_node) { + dev_err(rx_priv->dev, + "%s: DT node for RX dev does not exist\n", __func__); + return; + } + + platdata = &rx_priv->swr_plat_data; + rx_priv->child_count = 0; + + for_each_available_child_of_node(rx_priv->dev->of_node, node) { + rx_swr_master_node = false; + if (strnstr(node->name, "rx_swr_master", + strlen("rx_swr_master")) != NULL) + rx_swr_master_node = true; + + if(rx_swr_master_node) + strlcpy(plat_dev_name, "rx_swr_ctrl", + (RX_SWR_STRING_LEN - 1)); + else + strlcpy(plat_dev_name, node->name, + (RX_SWR_STRING_LEN - 1)); + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(rx_priv->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = rx_priv->dev; + pdev->dev.of_node = node; + + if (rx_swr_master_node) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (rx_swr_master_node) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct rx_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto fail_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].rx_swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + rx_priv->swr_ctrl_data = swr_ctrl_data; + } + if (rx_priv->child_count < RX_MACRO_CHILD_DEVICES_MAX) + rx_priv->pdev_child_devices[ + rx_priv->child_count++] = pdev; + else + goto err; + } + return; +fail_pdev_add: + for (count = 0; count < rx_priv->child_count; count++) + platform_device_put(rx_priv->pdev_child_devices[count]); +err: + return; +} + +static void rx_macro_init_ops(struct macro_ops *ops, char __iomem *rx_io_base) +{ + memset(ops, 0, sizeof(struct macro_ops)); + ops->init = rx_macro_init; + ops->exit = rx_macro_deinit; + ops->io_base = rx_io_base; + ops->dai_ptr = rx_macro_dai; + ops->num_dais = ARRAY_SIZE(rx_macro_dai); + ops->mclk_fn = rx_macro_mclk_ctrl; +} + +static int rx_macro_probe(struct platform_device *pdev) +{ + struct macro_ops ops = {0}; + struct rx_macro_priv *rx_priv = NULL; + u32 rx_base_addr = 0, muxsel = 0; + char __iomem *rx_io_base = NULL, *muxsel_io = NULL; + int ret = 0; + struct clk *rx_core_clk = NULL, *rx_npl_clk = NULL; + + rx_priv = devm_kzalloc(&pdev->dev, sizeof(struct rx_macro_priv), + GFP_KERNEL); + if (!rx_priv) + return -ENOMEM; + + rx_priv->dev = &pdev->dev; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &rx_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + ret = of_property_read_u32(pdev->dev.of_node, "qcom,rx_mclk_mode_muxsel", + &muxsel); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + rx_priv->rx_swr_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,rx-swr-gpios", 0); + if (!rx_priv->rx_swr_gpio_p) { + dev_err(&pdev->dev, "%s: swr_gpios handle not provided!\n", + __func__); + return -EINVAL; + } + rx_io_base = devm_ioremap(&pdev->dev, rx_base_addr, + RX_MACRO_MAX_OFFSET); + if (!rx_io_base) { + dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); + return -ENOMEM; + } + rx_priv->rx_io_base = rx_io_base; + muxsel_io = devm_ioremap(&pdev->dev, muxsel, 0x4); + if (!muxsel_io) { + dev_err(&pdev->dev, "%s: ioremap failed for muxsel\n", + __func__); + return -ENOMEM; + } + rx_priv->rx_mclk_mode_muxsel = muxsel_io; + INIT_WORK(&rx_priv->rx_macro_add_child_devices_work, + rx_macro_add_child_devices); + rx_priv->swr_plat_data.handle = (void *) rx_priv; + rx_priv->swr_plat_data.read = NULL; + rx_priv->swr_plat_data.write = NULL; + rx_priv->swr_plat_data.bulk_write = NULL; + rx_priv->swr_plat_data.clk = rx_swrm_clock; + rx_priv->swr_plat_data.handle_irq = NULL; + + /* Register MCLK for rx macro */ + rx_core_clk = devm_clk_get(&pdev->dev, "rx_core_clk"); + if (IS_ERR(rx_core_clk)) { + ret = PTR_ERR(rx_core_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "rx_core_clk", ret); + return ret; + } + rx_priv->rx_core_clk = rx_core_clk; + /* Register npl clk for soundwire */ + rx_npl_clk = devm_clk_get(&pdev->dev, "rx_npl_clk"); + if (IS_ERR(rx_npl_clk)) { + ret = PTR_ERR(rx_npl_clk); + dev_err(&pdev->dev, "%s: clk get %s failed %d\n", + __func__, "rx_npl_clk", ret); + return ret; + } + rx_priv->rx_npl_clk = rx_npl_clk; + dev_set_drvdata(&pdev->dev, rx_priv); + mutex_init(&rx_priv->mclk_lock); + mutex_init(&rx_priv->swr_clk_lock); + rx_macro_init_ops(&ops, rx_io_base); + + ret = bolero_register_macro(&pdev->dev, RX_MACRO, &ops); + if (ret) { + dev_err(&pdev->dev, + "%s: register macro failed\n", __func__); + goto err_reg_macro; + } + schedule_work(&rx_priv->rx_macro_add_child_devices_work); + + return 0; + +err_reg_macro: + mutex_destroy(&rx_priv->mclk_lock); + mutex_destroy(&rx_priv->swr_clk_lock); + return ret; +} + +static int rx_macro_remove(struct platform_device *pdev) +{ + struct rx_macro_priv *rx_priv = NULL; + u16 count = 0; + + rx_priv = dev_get_drvdata(&pdev->dev); + + if (!rx_priv) + return -EINVAL; + + for (count = 0; count < rx_priv->child_count && + count < RX_MACRO_CHILD_DEVICES_MAX; count++) + platform_device_unregister(rx_priv->pdev_child_devices[count]); + + bolero_unregister_macro(&pdev->dev, RX_MACRO); + mutex_destroy(&rx_priv->mclk_lock); + mutex_destroy(&rx_priv->swr_clk_lock); + kfree(rx_priv->swr_ctrl_data); + return 0; +} + +static const struct of_device_id rx_macro_dt_match[] = { + {.compatible = "qcom,rx-macro"}, + {} +}; + +static struct platform_driver rx_macro_driver = { + .driver = { + .name = "rx_macro", + .owner = THIS_MODULE, + .of_match_table = rx_macro_dt_match, + }, + .probe = rx_macro_probe, + .remove = rx_macro_remove, +}; + +module_platform_driver(rx_macro_driver); + +MODULE_DESCRIPTION("RX macro driver"); +MODULE_LICENSE("GPL v2");