Merge "dsp: codecs: fix range check for audio buffer copying"
diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c
index e86b30e..6ed5ddd 100644
--- a/asoc/codecs/audio-ext-clk-up.c
+++ b/asoc/codecs/audio-ext-clk-up.c
@@ -194,6 +194,7 @@
"qpnp_clkdiv_1",
"pms405_div_clk1",
"pm6150_div_clk1",
+ "pm6125_div_clk1",
};
static int audio_ext_clk_dummy_prepare(struct clk_hw *hw)
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index f1283f3..a21b57b 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -103,7 +103,8 @@
goto err;
}
- pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
+ if (priv->macro_params[VA_MACRO].dev)
+ pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
current_mclk_mux_macro =
priv->current_mclk_mux_macro[macro_id];
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -125,8 +126,10 @@
priv->macro_params[current_mclk_mux_macro].mclk_fn(
priv->macro_params[current_mclk_mux_macro].dev, false);
err:
- pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
- pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
+ if (priv->macro_params[VA_MACRO].dev) {
+ pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+ pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
+ }
mutex_unlock(&priv->clk_lock);
return ret;
}
@@ -143,7 +146,8 @@
"%s: SSR in progress, exit\n", __func__);
goto err;
}
- ret = pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
+ if (priv->macro_params[VA_MACRO].dev)
+ ret = pm_runtime_get_sync(priv->macro_params[VA_MACRO].dev);
current_mclk_mux_macro =
priv->current_mclk_mux_macro[macro_id];
if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -165,8 +169,10 @@
priv->macro_params[current_mclk_mux_macro].mclk_fn(
priv->macro_params[current_mclk_mux_macro].dev, false);
err:
- pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
- pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
+ if (priv->macro_params[VA_MACRO].dev) {
+ pm_runtime_mark_last_busy(priv->macro_params[VA_MACRO].dev);
+ pm_runtime_put_autosuspend(priv->macro_params[VA_MACRO].dev);
+ }
mutex_unlock(&priv->clk_lock);
return ret;
}
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index 4d9ccb4..0352152 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -453,6 +453,10 @@
SOC_DAPM_SINGLE("RX AUX VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
};
+static const char * const hph_idle_detect_text[] = {"OFF", "ON"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text);
+
RX_MACRO_DAPM_ENUM(rx_int0_2, BOLERO_CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
rx_int_mix_mux_text);
RX_MACRO_DAPM_ENUM(rx_int1_2, BOLERO_CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
@@ -1684,6 +1688,40 @@
}
}
+static int rx_macro_hph_idle_detect_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro_priv *rx_priv = NULL;
+ struct device *rx_dev = NULL;
+
+ if (!rx_macro_get_data(component, &rx_dev, &rx_priv, __func__))
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] =
+ rx_priv->idle_det_cfg.hph_idle_detect_en;
+
+ return 0;
+}
+
+static int rx_macro_hph_idle_detect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct rx_macro_priv *rx_priv = NULL;
+ struct device *rx_dev = NULL;
+
+ if (!rx_macro_get_data(component, &rx_dev, &rx_priv, __func__))
+ return -EINVAL;
+
+ rx_priv->idle_det_cfg.hph_idle_detect_en =
+ ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
static int rx_macro_get_compander(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2611,6 +2649,9 @@
SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0,
rx_macro_get_compander, rx_macro_set_compander),
+ SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum,
+ rx_macro_hph_idle_detect_get, rx_macro_hph_idle_detect_put),
+
SOC_ENUM_EXT("RX_EAR Mode", rx_macro_ear_mode_enum,
rx_macro_get_ear_mode, rx_macro_put_ear_mode),
@@ -3221,8 +3262,12 @@
__func__, (enable ? "enable" : "disable"));
if (enable) {
if (rx_priv->swr_clk_users == 0) {
+ msm_cdc_pinctrl_select_active_state(
+ rx_priv->rx_swr_gpio_p);
ret = rx_macro_mclk_enable(rx_priv, 1, true);
if (ret < 0) {
+ msm_cdc_pinctrl_select_sleep_state(
+ rx_priv->rx_swr_gpio_p);
dev_err(rx_priv->dev,
"%s: rx request clock enable failed\n",
__func__);
@@ -3240,8 +3285,6 @@
BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
0x02, 0x00);
rx_priv->reset_swr = false;
- msm_cdc_pinctrl_select_active_state(
- rx_priv->rx_swr_gpio_p);
}
rx_priv->swr_clk_users++;
} else {
@@ -3257,9 +3300,9 @@
regmap_update_bits(regmap,
BOLERO_CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
0x01, 0x00);
+ rx_macro_mclk_enable(rx_priv, 0, true);
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",
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index b24721f..58f3689 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -1437,8 +1437,12 @@
__func__, (enable ? "enable" : "disable"));
if (enable) {
if (tx_priv->swr_clk_users == 0) {
+ msm_cdc_pinctrl_select_active_state(
+ tx_priv->tx_swr_gpio_p);
ret = tx_macro_mclk_enable(tx_priv, 1);
if (ret < 0) {
+ msm_cdc_pinctrl_select_sleep_state(
+ tx_priv->tx_swr_gpio_p);
dev_err(tx_priv->dev,
"%s: request clock enable failed\n",
__func__);
@@ -1456,11 +1460,6 @@
BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
0x02, 0x00);
tx_priv->reset_swr = false;
- regmap_update_bits(regmap,
- BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
- 0x1C, 0x0C);
- msm_cdc_pinctrl_select_active_state(
- tx_priv->tx_swr_gpio_p);
}
tx_priv->swr_clk_users++;
} else {
@@ -1475,9 +1474,9 @@
regmap_update_bits(regmap,
BOLERO_CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
0x01, 0x00);
+ tx_macro_mclk_enable(tx_priv, 0);
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",
diff --git a/asoc/codecs/bolero/wsa-macro.c b/asoc/codecs/bolero/wsa-macro.c
index 00d3d2c..b0a4b1e 100644
--- a/asoc/codecs/bolero/wsa-macro.c
+++ b/asoc/codecs/bolero/wsa-macro.c
@@ -2570,8 +2570,12 @@
__func__, (enable ? "enable" : "disable"));
if (enable) {
if (wsa_priv->swr_clk_users == 0) {
+ msm_cdc_pinctrl_select_active_state(
+ wsa_priv->wsa_swr_gpio_p);
ret = wsa_macro_mclk_enable(wsa_priv, 1, true);
if (ret < 0) {
+ msm_cdc_pinctrl_select_sleep_state(
+ wsa_priv->wsa_swr_gpio_p);
dev_err(wsa_priv->dev,
"%s: wsa request clock enable failed\n",
__func__);
@@ -2589,11 +2593,6 @@
BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
0x02, 0x00);
wsa_priv->reset_swr = false;
- regmap_update_bits(regmap,
- BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
- 0x1C, 0x0C);
- msm_cdc_pinctrl_select_active_state(
- wsa_priv->wsa_swr_gpio_p);
}
wsa_priv->swr_clk_users++;
} else {
@@ -2608,9 +2607,9 @@
regmap_update_bits(regmap,
BOLERO_CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
0x01, 0x00);
+ wsa_macro_mclk_enable(wsa_priv, 0, true);
msm_cdc_pinctrl_select_sleep_state(
wsa_priv->wsa_swr_gpio_p);
- wsa_macro_mclk_enable(wsa_priv, 0, true);
}
}
dev_dbg(wsa_priv->dev, "%s: swrm clock users %d\n",
diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c
index 6839203..1159b11 100644
--- a/asoc/codecs/wcd-mbhc-v2.c
+++ b/asoc/codecs/wcd-mbhc-v2.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/init.h>
@@ -850,7 +850,7 @@
{
bool ret = false;
- if (!mbhc->mbhc_cfg->moisture_en ||
+ if (!mbhc->mbhc_cfg->moisture_en &&
!mbhc->mbhc_cfg->moisture_duty_cycle_en)
return ret;
@@ -1342,9 +1342,15 @@
else
WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
+ /* Configure for moisture detection when duty cycle is not enabled.
+ * Otherwise disable moisture detection.
+ */
if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config
&& !mbhc->mbhc_cfg->moisture_duty_cycle_en)
mbhc->mbhc_cb->mbhc_moisture_config(mbhc);
+ else if (mbhc->mbhc_cfg->moisture_duty_cycle_en &&
+ mbhc->mbhc_cb->mbhc_moisture_detect_en)
+ mbhc->mbhc_cb->mbhc_moisture_detect_en(mbhc, false);
/*
* For USB analog we need to override the switch configuration.
diff --git a/asoc/codecs/wcd937x/wcd937x.c b/asoc/codecs/wcd937x/wcd937x.c
index ae286bd..beb4b17 100644
--- a/asoc/codecs/wcd937x/wcd937x.c
+++ b/asoc/codecs/wcd937x/wcd937x.c
@@ -131,7 +131,9 @@
0xFF, 0x3A);
snd_soc_component_update_bits(component, WCD937X_RX_OCP_CTL,
0x0F, 0x02);
-
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_SURGE_HPHLR_SURGE_EN,
+ 0xFF, 0xD9);
return 0;
}
@@ -1507,6 +1509,10 @@
wcd937x_get_logical_addr(wcd937x->rx_swr_dev);
regcache_mark_dirty(wcd937x->regmap);
regcache_sync(wcd937x->regmap);
+ /* Enable surge protection */
+ snd_soc_component_update_bits(component,
+ WCD937X_HPH_SURGE_HPHLR_SURGE_EN,
+ 0xFF, 0xD9);
/* Initialize MBHC module */
mbhc = &wcd937x->mbhc->wcd_mbhc;
ret = wcd937x_mbhc_post_ssr_init(wcd937x->mbhc, component);
diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c
index 8ebef1f..8a5622f 100644
--- a/asoc/codecs/wcd938x/wcd938x.c
+++ b/asoc/codecs/wcd938x/wcd938x.c
@@ -49,6 +49,10 @@
};
enum {
+ WCD_ADC1 = 0,
+ WCD_ADC2,
+ WCD_ADC3,
+ WCD_ADC4,
ALLOW_BUCK_DISABLE,
HPH_COMP_DELAY,
HPH_PA_DELAY,
@@ -1227,12 +1231,14 @@
default:
break;
}
+ set_bit(w->shift, &wcd938x->status_mask);
wcd938x_tx_connect_port(component, ADC1 + (w->shift), true);
break;
case SND_SOC_DAPM_POST_PMD:
wcd938x_tx_connect_port(component, ADC1 + (w->shift), false);
snd_soc_component_update_bits(component,
WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x08, 0x00);
+ clear_bit(w->shift, &wcd938x->status_mask);
break;
};
@@ -1469,8 +1475,6 @@
void *data)
{
u16 event = (val & 0xffff);
- u16 amic;
- u16 mask = 0x40, reg = 0x0;
int ret = 0;
struct wcd938x_priv *wcd938x = dev_get_drvdata((struct device *)data);
struct snd_soc_component *component = wcd938x->component;
@@ -1478,16 +1482,26 @@
switch (event) {
case BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR:
- amic = (val >> 0x10);
- if (amic == 0x1 || amic == 0x2)
- reg = WCD938X_ANA_TX_CH2;
- else if (amic == 0x3)
- reg = WCD938X_ANA_TX_CH4;
- else
- return 0;
- if (amic == 0x2)
- mask = 0x20;
- snd_soc_component_update_bits(component, reg, mask, 0x00);
+ if (test_bit(WCD_ADC1, &wcd938x->status_mask)) {
+ snd_soc_component_update_bits(component,
+ WCD938X_ANA_TX_CH2, 0x40, 0x00);
+ clear_bit(WCD_ADC1, &wcd938x->status_mask);
+ }
+ if (test_bit(WCD_ADC2, &wcd938x->status_mask)) {
+ snd_soc_component_update_bits(component,
+ WCD938X_ANA_TX_CH2, 0x20, 0x00);
+ clear_bit(WCD_ADC2, &wcd938x->status_mask);
+ }
+ if (test_bit(WCD_ADC3, &wcd938x->status_mask)) {
+ snd_soc_component_update_bits(component,
+ WCD938X_ANA_TX_CH4, 0x40, 0x00);
+ clear_bit(WCD_ADC3, &wcd938x->status_mask);
+ }
+ if (test_bit(WCD_ADC4, &wcd938x->status_mask)) {
+ snd_soc_component_update_bits(component,
+ WCD938X_ANA_TX_CH4, 0x20, 0x00);
+ clear_bit(WCD_ADC4, &wcd938x->status_mask);
+ }
break;
case BOLERO_WCD_EVT_PA_OFF_PRE_SSR:
snd_soc_component_update_bits(component, WCD938X_ANA_HPH,
diff --git a/asoc/kona.c b/asoc/kona.c
index d8c81fc..6935358 100644
--- a/asoc/kona.c
+++ b/asoc/kona.c
@@ -4058,7 +4058,6 @@
.ignore_pmdown_time = 1,
.id = MSM_FRONTEND_DAI_MULTIMEDIA3,
},
- /* Hostless PCM purpose */
{/* hw:x,5 */
.name = "MSM AFE-PCM RX",
.stream_name = "AFE-PROXY RX",
@@ -4099,6 +4098,7 @@
/* this dainlink has playback support */
.id = MSM_FRONTEND_DAI_MULTIMEDIA4,
},
+ /* Hostless PCM purpose */
{/* hw:x,8 */
.name = "AUXPCM Hostless",
.stream_name = "AUXPCM Hostless",
@@ -4486,10 +4486,25 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ {/* hw:x,32 */
+ .name = "Tertiary MI2S TX_Hostless",
+ .stream_name = "Tertiary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
};
static struct snd_soc_dai_link msm_bolero_fe_dai_links[] = {
- {/* hw:x,37 */
+ {/* hw:x,33 */
.name = LPASS_BE_WSA_CDC_DMA_TX_0,
.stream_name = "WSA CDC DMA0 Capture",
.cpu_dai_name = "msm-dai-cdc-dma-dev.45057",
@@ -4505,7 +4520,7 @@
};
static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
- {
+ {/* hw:x,34 */
.name = MSM_DAILINK_NAME(ASM Loopback),
.stream_name = "MultiMedia6",
.cpu_dai_name = "MultiMedia6",
@@ -4522,7 +4537,7 @@
.ignore_pmdown_time = 1,
.id = MSM_FRONTEND_DAI_MULTIMEDIA6,
},
- {
+ {/* hw:x,35 */
.name = "USB Audio Hostless",
.stream_name = "USB Audio Hostless",
.cpu_dai_name = "USBAUDIO_HOSTLESS",
@@ -4538,7 +4553,7 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
- {
+ {/* hw:x,36 */
.name = "SLIMBUS_7 Hostless",
.stream_name = "SLIMBUS_7 Hostless",
.cpu_dai_name = "SLIMBUS7_HOSTLESS",
@@ -4554,7 +4569,7 @@
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
- {
+ {/* hw:x,37 */
.name = "Compress Capture",
.stream_name = "Compress9",
.cpu_dai_name = "MultiMedia17",
diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c
index 2bde61a..be10bc1 100644
--- a/asoc/msm-compress-q6-v2.c
+++ b/asoc/msm-compress-q6-v2.c
@@ -101,6 +101,7 @@
struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
bool is_in_use[MSM_FRONTEND_DAI_MAX];
+ struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE];
};
struct msm_compr_audio {
@@ -3711,6 +3712,7 @@
struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
snd_soc_component_get_drvdata(comp);
int rc = 0, i;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
@@ -3726,6 +3728,17 @@
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
pdata->ch_map[fe_id]->channel_map[i] =
(char)(ucontrol->value.integer.value[i]);
+
+ /* update chmixer_pspd chmap cached with routing driver as well */
+ chmixer_pspd = pdata->chmixer_pspd[fe_id];
+ if (chmixer_pspd && chmixer_pspd->enable) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ chmixer_pspd->override_in_ch_map = true;
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ SESSION_TYPE_RX, chmixer_pspd);
+ }
} else {
pr_debug("%s: no memory for ch_map, default will be set\n",
__func__);
@@ -3971,10 +3984,11 @@
const char *qdsp_version;
pr_debug("%s\n", __func__);
- pdata = (struct msm_compr_pdata *)
- kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
+ pdata = (struct msm_compr_pdata *) dev_get_drvdata(component->dev);
+ if (!pdata) {
+ pr_err("%s platform data not set\n", __func__);
+ return -EINVAL;
+ }
snd_soc_component_set_drvdata(component, pdata);
@@ -4068,8 +4082,8 @@
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
uinfo->value.integer.min = 0;
- /* See PCM_CHANNEL_RSD=34 in apr_audio-v2.h */
- uinfo->value.integer.max = 34;
+ /* See PCM_CHANNEL_UNUSED=47 in apr_audio-v2.h */
+ uinfo->value.integer.max = 47;
return 0;
}
@@ -4640,6 +4654,580 @@
return ret;
}
+static struct msm_pcm_channel_mixer *msm_compr_get_chmixer(
+ struct msm_compr_pdata *pdata, u64 fe_id)
+{
+ if (!pdata) {
+ pr_err("%s: missing pdata\n", __func__);
+ return NULL;
+ }
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return NULL;
+ }
+
+ return pdata->chmixer_pspd[fe_id];
+}
+
+static int msm_compr_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int ret = 0, i = 0, stream_id = 0, be_id = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+ u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+ bool reset_override_out_ch_map = false;
+ bool reset_override_in_ch_map = false;
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->enable = ucontrol->value.integer.value[0];
+ chmixer_pspd->rule = ucontrol->value.integer.value[1];
+ chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+ chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+ chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+ if (chmixer_pspd->enable) {
+ if (session_type == SESSION_TYPE_RX &&
+ !chmixer_pspd->override_in_ch_map) {
+ if (pdata->ch_map[fe_id]->set_ch_map) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ } else {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->input_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+ }
+ chmixer_pspd->override_in_ch_map = true;
+ reset_override_in_ch_map = true;
+ } else if (session_type == SESSION_TYPE_TX &&
+ !chmixer_pspd->override_out_ch_map) {
+ if (pdata->ch_map[fe_id]->set_ch_map) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ } else {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->output_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+ }
+ chmixer_pspd->override_out_ch_map = true;
+ reset_override_out_ch_map = true;
+ }
+ } else {
+ chmixer_pspd->override_out_ch_map = false;
+ chmixer_pspd->override_in_ch_map = false;
+ }
+
+ /* cache value and take effect during adm_open stage */
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ session_type,
+ chmixer_pspd);
+
+ cstream = pdata->cstream[fe_id];
+ if (chmixer_pspd->enable && cstream && cstream->runtime) {
+ prtd = cstream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s invalid prtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client) {
+ stream_id = prtd->audio_client->session;
+ be_id = chmixer_pspd->port_idx;
+ ret = msm_pcm_routing_set_channel_mixer_runtime(be_id,
+ stream_id, session_type, chmixer_pspd);
+ }
+ }
+
+ if (reset_override_out_ch_map)
+ chmixer_pspd->override_out_ch_map = false;
+ if (reset_override_in_ch_map)
+ chmixer_pspd->override_in_ch_map = false;
+
+done:
+ return ret;
+}
+
+static int msm_compr_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+ ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+ ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+ ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+ ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+ return 0;
+}
+
+static int msm_compr_channel_mixer_output_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_out_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] =
+ ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_compr_channel_mixer_output_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->out_ch_map[i];
+ return 0;
+}
+
+static int msm_compr_channel_mixer_input_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_in_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_compr_channel_mixer_input_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->in_ch_map[i];
+ return 0;
+}
+
+static int msm_compr_channel_mixer_weight_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->channel_weight[channel][i] =
+ ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int msm_compr_channel_mixer_weight_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct msm_compr_pdata *pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(comp);
+ int i = 0;
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ chmixer_pspd = msm_compr_get_chmixer(pdata, fe_id);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->channel_weight[channel][i];
+ return 0;
+}
+
+static int msm_compr_add_platform_controls(struct snd_kcontrol_new *kctl,
+ struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+ const char *name_suffix, int session_type, int channels)
+{
+ int ret = -EINVAL;
+ char *mixer_name = NULL;
+ const char *deviceNo = "NN";
+ const char *channelNo = "NN";
+ int ctl_len = 0;
+ struct snd_soc_component *component = NULL;
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+ strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+ mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+ if (mixer_name == NULL)
+ return -ENOMEM;
+
+ if (channels >= 0) {
+ snprintf(mixer_name, ctl_len, "%s %d %s %d",
+ name_prefix, rtd->pcm->device, name_suffix, channels);
+ kctl->private_value = (rtd->dai_link->id) | (channels << 16);
+ } else {
+ snprintf(mixer_name, ctl_len, "%s %d %s",
+ name_prefix, rtd->pcm->device, name_suffix);
+ kctl->private_value = (rtd->dai_link->id);
+ }
+ if (session_type != INVALID_SESSION)
+ kctl->private_value |= (session_type << 8);
+
+ kctl->name = mixer_name;
+ ret = snd_soc_add_component_controls(component, kctl, 1);
+ kfree(mixer_name);
+ return ret;
+}
+
+static int msm_compr_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_compr_add_channel_mixer_output_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Output Map";
+ const char *mixer_ctl_name = NULL;
+ int ret = 0, session_type = INVALID_SESSION, channel = -1;
+ struct snd_kcontrol_new channel_mixer_output_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_channel_mixer_output_map_info,
+ .put = msm_compr_channel_mixer_output_map_ctl_put,
+ .get = msm_compr_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ };
+
+ mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+ playback_mixer_ctl_name : capture_mixer_ctl_name ;
+ ret = msm_compr_add_platform_controls(&channel_mixer_output_map_control,
+ rtd, mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int msm_compr_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_compr_add_channel_mixer_input_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Input Map";
+ const char *mixer_ctl_name = NULL;
+ int ret = 0, session_type = INVALID_SESSION, channel = -1;
+ struct snd_kcontrol_new channel_mixer_input_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_channel_mixer_input_map_info,
+ .put = msm_compr_channel_mixer_input_map_ctl_put,
+ .get = msm_compr_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ };
+
+ mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+ playback_mixer_ctl_name : capture_mixer_ctl_name ;
+ ret = msm_compr_add_platform_controls(&channel_mixer_input_map_control,
+ rtd, mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int msm_compr_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* five int values: enable, rule, in_channels, out_channels and port_id */
+ uinfo->count = 5;
+ /* Valid range is all positive values to support above controls */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = INT_MAX;
+ return 0;
+}
+
+static int msm_compr_add_channel_mixer_cfg_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Cfg";
+ const char *mixer_ctl_name = NULL;
+ int ret = 0, session_type = INVALID_SESSION, channel = -1;
+ struct snd_kcontrol_new channel_mixer_cfg_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_channel_mixer_cfg_info,
+ .put = msm_compr_channel_mixer_cfg_ctl_put,
+ .get = msm_compr_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ };
+
+ if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+ session_type = SESSION_TYPE_RX;
+ mixer_ctl_name = playback_mixer_ctl_name;
+ } else {
+ session_type = SESSION_TYPE_TX;
+ mixer_ctl_name = capture_mixer_ctl_name;
+ }
+
+ ret = msm_compr_add_platform_controls(&channel_mixer_cfg_control,
+ rtd, mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int msm_compr_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid range: 0 to 0x4000(Unity) gain weightage */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x4000;
+ return 0;
+}
+
+static int msm_compr_add_channel_mixer_weight_controls(
+ struct snd_soc_pcm_runtime *rtd,
+ int channel)
+{
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Weight Ch";
+ const char *mixer_ctl_name = NULL;
+ int ret = 0, session_type = INVALID_SESSION;
+ struct snd_kcontrol_new channel_mixer_weight_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_compr_channel_mixer_weight_info,
+ .put = msm_compr_channel_mixer_weight_ctl_put,
+ .get = msm_compr_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ };
+
+ mixer_ctl_name = rtd->compr->direction == SND_COMPRESS_PLAYBACK ?
+ playback_mixer_ctl_name : capture_mixer_ctl_name ;
+ ret = msm_compr_add_platform_controls(&channel_mixer_weight_control,
+ rtd, mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0) {
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int msm_compr_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret = 0;
+ struct msm_compr_pdata *pdata = NULL;
+ struct snd_soc_component *component = NULL;
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ return -EINVAL;
+ }
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pdata = (struct msm_compr_pdata *)
+ snd_soc_component_get_drvdata(component);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!pdata->chmixer_pspd[rtd->dai_link->id]) {
+ pdata->chmixer_pspd[rtd->dai_link->id] =
+ kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+ if (!pdata->chmixer_pspd[rtd->dai_link->id])
+ return -ENOMEM;
+ }
+
+ ret = msm_compr_add_channel_mixer_cfg_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_compr_add_channel_mixer_input_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_compr_add_channel_mixer_output_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+ ret = msm_compr_add_channel_mixer_weight_controls(rtd, i);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ }
+ return 0;
+
+fail:
+ kfree(pdata->chmixer_pspd[rtd->dai_link->id]);
+ pdata->chmixer_pspd[rtd->dai_link->id] = NULL;
+ return ret;
+}
+
static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
{
int rc;
@@ -4690,6 +5278,10 @@
if (rc)
pr_err("%s: Could not add Compr Channel Map Control\n",
__func__);
+ rc = msm_compr_add_channel_mixer_controls(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr Channel Mixer Controls\n",
+ __func__);
return 0;
}
@@ -4717,14 +5309,31 @@
static int msm_compr_dev_probe(struct platform_device *pdev)
{
+ struct msm_compr_pdata *pdata = NULL;
pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+ pdata = (struct msm_compr_pdata *)
+ kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ dev_set_drvdata(&pdev->dev, pdata);
+
return snd_soc_register_component(&pdev->dev,
&msm_soc_component, NULL, 0);
}
static int msm_compr_remove(struct platform_device *pdev)
{
+ int i = 0;
+ struct msm_compr_pdata *pdata = NULL;
+
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata) {
+ for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++)
+ kfree(pdata->chmixer_pspd[i]);
+ }
+ kfree(pdata);
+
snd_soc_unregister_component(&pdev->dev);
return 0;
}
diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c
index 10acd50..c2be85b 100644
--- a/asoc/msm-dai-q6-v2.c
+++ b/asoc/msm-dai-q6-v2.c
@@ -3226,11 +3226,64 @@
return 0;
}
+static int msm_dai_q6_afe_feedback_dec_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ u32 format_size = 0;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+
+ format_size = sizeof(dai_data->dec_config.format);
+ memcpy(ucontrol->value.bytes.data,
+ &dai_data->dec_config.format,
+ format_size);
+
+ pr_debug("%s: abr_dec_cfg for %d format\n",
+ __func__, dai_data->dec_config.format);
+ memcpy(ucontrol->value.bytes.data + format_size,
+ &dai_data->dec_config.abr_dec_cfg,
+ sizeof(struct afe_imc_dec_enc_info));
+
+ return 0;
+}
+
+static int msm_dai_q6_afe_feedback_dec_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+ u32 format_size = 0;
+
+ if (!dai_data) {
+ pr_err("%s: Invalid dai data\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&dai_data->dec_config, 0x0,
+ sizeof(struct afe_dec_config));
+ format_size = sizeof(dai_data->dec_config.format);
+ memcpy(&dai_data->dec_config.format,
+ ucontrol->value.bytes.data,
+ format_size);
+
+ pr_debug("%s: abr_dec_cfg for %d format\n",
+ __func__, dai_data->dec_config.format);
+ memcpy(&dai_data->dec_config.abr_dec_cfg,
+ ucontrol->value.bytes.data + format_size,
+ sizeof(struct afe_imc_dec_enc_info));
+ dai_data->dec_config.abr_dec_cfg.is_abr_enabled = true;
+ return 0;
+}
+
static int msm_dai_q6_afe_dec_cfg_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
u32 format_size = 0;
+ int ret = 0;
if (!dai_data) {
pr_err("%s: Invalid dai data\n", __func__);
@@ -3247,20 +3300,23 @@
&dai_data->dec_config.data,
sizeof(struct asm_aac_dec_cfg_v2_t));
break;
+ case DEC_FMT_APTX_ADAPTIVE:
+ memcpy(ucontrol->value.bytes.data + format_size,
+ &dai_data->dec_config.data,
+ sizeof(struct asm_aptx_ad_dec_cfg_t));
+ break;
case DEC_FMT_SBC:
case DEC_FMT_MP3:
/* No decoder specific data available */
break;
default:
- pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n",
+ pr_err("%s: Invalid format %d\n",
__func__, dai_data->dec_config.format);
- memcpy(ucontrol->value.bytes.data + format_size,
- &dai_data->dec_config.abr_dec_cfg,
- sizeof(struct afe_abr_dec_cfg_t));
-
+ ret = -EINVAL;
break;
}
- return 0;
+
+ return ret;
}
static int msm_dai_q6_afe_dec_cfg_put(struct snd_kcontrol *kcontrol,
@@ -3268,6 +3324,7 @@
{
struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
u32 format_size = 0;
+ int ret = 0;
if (!dai_data) {
pr_err("%s: Invalid dai data\n", __func__);
@@ -3293,15 +3350,19 @@
ucontrol->value.bytes.data + format_size,
sizeof(struct asm_sbc_dec_cfg_t));
break;
- default:
- pr_debug("%s: Default decoder config for %d format: Expect abr_dec_cfg\n",
- __func__, dai_data->dec_config.format);
- memcpy(&dai_data->dec_config.abr_dec_cfg,
+ case DEC_FMT_APTX_ADAPTIVE:
+ memcpy(&dai_data->dec_config.data,
ucontrol->value.bytes.data + format_size,
- sizeof(struct afe_abr_dec_cfg_t));
+ sizeof(struct asm_aptx_ad_dec_cfg_t));
+ break;
+ default:
+ pr_err("%s: Invalid format %d\n",
+ __func__, dai_data->dec_config.format);
+ ret = -EINVAL;
break;
}
- return 0;
+
+ return ret;
}
static const struct snd_kcontrol_new afe_dec_config_controls[] = {
@@ -3311,8 +3372,8 @@
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "SLIM_7_TX Decoder Config",
.info = msm_dai_q6_afe_dec_cfg_info,
- .get = msm_dai_q6_afe_dec_cfg_get,
- .put = msm_dai_q6_afe_dec_cfg_put,
+ .get = msm_dai_q6_afe_feedback_dec_cfg_get,
+ .put = msm_dai_q6_afe_feedback_dec_cfg_put,
},
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c
index 6bf44c4..379b5e5 100644
--- a/asoc/msm-lsm-client.c
+++ b/asoc/msm-lsm-client.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
#include <linux/err.h>
@@ -1050,7 +1050,7 @@
if (ses_data_v2.app_id != LSM_VOICE_WAKEUP_APP_ID_V2) {
dev_err(rtd->dev,
"%s:Invalid App id %d for Listen client\n",
- __func__, session_data.app_id);
+ __func__, ses_data_v2.app_id);
rc = -EINVAL;
break;
}
diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c
index b516634..a686984 100644
--- a/asoc/msm-pcm-loopback-v2.c
+++ b/asoc/msm-pcm-loopback-v2.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/init.h>
@@ -64,6 +64,8 @@
struct msm_pcm_pdata {
int perf_mode;
+ struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+ struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
};
static void stop_pcm(struct msm_pcm_loopback *pcm);
@@ -734,6 +736,671 @@
return 0;
}
+static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
+ struct msm_pcm_pdata *pdata,
+ u64 fe_id, int session_type)
+{
+ if (!pdata) {
+ pr_err("%s: missing pdata\n", __func__);
+ return NULL;
+ }
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return NULL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return NULL;
+ }
+
+ return pdata->chmixer_pspd[fe_id][session_type];
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int ret = 0;
+ int stream_id = 0;
+ int be_id = 0, i = 0;
+ struct msm_pcm_loopback *prtd = NULL;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct snd_pcm *pcm = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+ u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+ bool reset_override_out_ch_map = false;
+ bool reset_override_in_ch_map = false;
+
+ pcm = pdata->pcm_device[fe_id];
+ if (!pcm) {
+ pr_err("%s invalid pcm handle for fe_id %llu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if (session_type == SESSION_TYPE_RX)
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ else
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->enable = ucontrol->value.integer.value[0];
+ chmixer_pspd->rule = ucontrol->value.integer.value[1];
+ chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+ chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+ chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+ if (chmixer_pspd->enable) {
+ if (session_type == SESSION_TYPE_RX &&
+ !chmixer_pspd->override_in_ch_map) {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->input_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+ chmixer_pspd->override_in_ch_map = true;
+ reset_override_in_ch_map = true;
+ } else if (session_type == SESSION_TYPE_TX &&
+ !chmixer_pspd->override_out_ch_map) {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->output_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+ chmixer_pspd->override_out_ch_map = true;
+ reset_override_out_ch_map = true;
+ }
+ } else {
+ chmixer_pspd->override_out_ch_map = false;
+ chmixer_pspd->override_in_ch_map = false;
+ }
+
+ /* cache value and take effect during adm_open stage */
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ session_type,
+ chmixer_pspd);
+
+ if (chmixer_pspd->enable && substream->runtime) {
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s find invalid prtd fail\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client) {
+ stream_id = prtd->audio_client->session;
+ be_id = chmixer_pspd->port_idx;
+ msm_pcm_routing_set_channel_mixer_runtime(be_id,
+ stream_id,
+ session_type,
+ chmixer_pspd);
+ }
+ }
+
+ if (reset_override_out_ch_map)
+ chmixer_pspd->override_out_ch_map = false;
+ if (reset_override_in_ch_map)
+ chmixer_pspd->override_in_ch_map = false;
+
+done:
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+ ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+ ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+ ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+ ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_out_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] =
+ ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->out_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_in_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->in_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->channel_weight[channel][i] =
+ ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct msm_pcm_pdata *pdata = dev_get_drvdata(component->dev);
+ int i = 0;
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->channel_weight[channel][i];
+ return 0;
+}
+
+static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
+ struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+ const char *name_suffix, int session_type, int channels)
+{
+ int ret = -EINVAL;
+ char *mixer_name = NULL;
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *deviceNo = "NN";
+ const char *channelNo = "NN";
+ int ctl_len = 0;
+ struct snd_soc_component *component = NULL;
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+ strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+ mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+ if (mixer_name == NULL)
+ return -ENOMEM;
+
+ if (channels >= 0) {
+ snprintf(mixer_name, ctl_len, "%s %d %s %d",
+ name_prefix, pcm->device, name_suffix, channels);
+ kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
+ (channels << 16);
+ } else {
+ snprintf(mixer_name, ctl_len, "%s %d %s",
+ name_prefix, pcm->device, name_suffix);
+ kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
+ }
+
+ kctl->name = mixer_name;
+ ret = snd_soc_add_component_controls(component, kctl, 1);
+ kfree(mixer_name);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Output Map";
+ int session_type = 0, ret = 0, channel = -1;
+ struct snd_kcontrol_new channel_mixer_output_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Input Map";
+ int session_type = 0, ret = 0, channel = -1;
+ struct snd_kcontrol_new channel_mixer_input_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* five int values: enable, rule, in_channels, out_channels and port_id */
+ uinfo->count = 5;
+ /* Valid range is all positive values to support above controls */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = INT_MAX;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Cfg";
+ int session_type = 0, ret = 0, channel = -1;
+ struct msm_pcm_pdata *pdata = NULL;
+ struct snd_soc_component *component = NULL;
+ struct snd_kcontrol_new channel_mixer_cfg_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ };
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+ pdata = (struct msm_pcm_pdata *)
+ dev_get_drvdata(component->dev);
+ if (pdata == NULL) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+
+ pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid range: 0 to 0x4000(Unity) gain weightage */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x4000;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+ struct snd_soc_pcm_runtime *rtd,
+ int channel)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Weight Ch";
+ int session_type = 0, ret = 0;
+ struct snd_kcontrol_new channel_mixer_weight_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret = 0;
+ struct snd_pcm *pcm = NULL;
+ struct msm_pcm_pdata *pdata = NULL;
+ struct snd_soc_component *component = NULL;
+
+ if (!rtd || !rtd->pcm) {
+ pr_err("%s invalid rtd or pcm\n", __func__);
+ return -EINVAL;
+ }
+ pcm = rtd->pcm;
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pdata = (struct msm_pcm_pdata *)
+ dev_get_drvdata(component->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
+ !pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
+ kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+ if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
+ !pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
+ kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+ if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+ ret = msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ }
+ return 0;
+
+fail:
+ kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
+ kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
+
+ return ret;
+}
+
static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
{
int ret = 0;
@@ -747,6 +1414,11 @@
if (ret)
pr_err("%s: pcm add app type controls failed:%d\n",
__func__, ret);
+
+ ret = msm_pcm_add_channel_mixer_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer controls failed:%d\n",
+ __func__, ret);
return ret;
}
@@ -797,6 +1469,17 @@
static int msm_pcm_remove(struct platform_device *pdev)
{
+ struct msm_pcm_pdata *pdata;
+ int i = 0;
+
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata) {
+ for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
+ kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
+ kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
+ }
+ }
+ kfree(pdata);
snd_soc_unregister_component(&pdev->dev);
return 0;
}
diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c
index 5886736..bea58c9 100644
--- a/asoc/msm-pcm-q6-v2.c
+++ b/asoc/msm-pcm-q6-v2.c
@@ -1601,6 +1601,11 @@
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_pcm_substream *substream;
struct msm_audio *prtd;
+ struct snd_soc_pcm_runtime *rtd = NULL;
+ struct msm_plat_data *pdata = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ u64 fe_id = 0;
pr_debug("%s", __func__);
substream = snd_pcm_chmap_substream(info, idx);
@@ -1615,6 +1620,24 @@
for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
prtd->channel_map[i] =
(char)(ucontrol->value.integer.value[i]);
+
+ /* update chmixer_pspd chmap cached with routing driver as well */
+ rtd = substream->private_data;
+ if (rtd) {
+ fe_id = rtd->dai_link->id;
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(component->dev);
+ chmixer_pspd = pdata ?
+ pdata->chmixer_pspd[fe_id][SESSION_TYPE_RX] : NULL;
+
+ if (chmixer_pspd && chmixer_pspd->enable) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = prtd->channel_map[i];
+ chmixer_pspd->override_in_ch_map = true;
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ SESSION_TYPE_RX, chmixer_pspd);
+ }
+ }
}
return 0;
}
@@ -1838,6 +1861,674 @@
return 0;
}
+static struct msm_pcm_channel_mixer *msm_pcm_get_chmixer(
+ struct msm_plat_data *pdata,
+ u64 fe_id, int session_type)
+{
+ if (!pdata) {
+ pr_err("%s: missing pdata\n", __func__);
+ return NULL;
+ }
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %llu\n", __func__, fe_id);
+ return NULL;
+ }
+
+ if ((session_type != SESSION_TYPE_TX) &&
+ (session_type != SESSION_TYPE_RX)) {
+ pr_err("%s: invalid session type %d\n", __func__, session_type);
+ return NULL;
+ }
+
+ return pdata->chmixer_pspd[fe_id][session_type];
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int ret = 0;
+ int stream_id = 0;
+ int be_id = 0, i = 0;
+ struct msm_audio *prtd = NULL;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct snd_pcm *pcm = NULL;
+ struct snd_pcm_substream *substream = NULL;
+ struct msm_pcm_channel_mixer *chmixer_pspd = NULL;
+ u8 asm_ch_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+ bool reset_override_out_ch_map = false;
+ bool reset_override_in_ch_map = false;
+
+ pcm = pdata->pcm_device[fe_id];
+ if (!pcm) {
+ pr_err("%s invalid pcm handle for fe_id %llu\n",
+ __func__, fe_id);
+ return -EINVAL;
+ }
+
+ if (session_type == SESSION_TYPE_RX)
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ else
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->enable = ucontrol->value.integer.value[0];
+ chmixer_pspd->rule = ucontrol->value.integer.value[1];
+ chmixer_pspd->input_channel = ucontrol->value.integer.value[2];
+ chmixer_pspd->output_channel = ucontrol->value.integer.value[3];
+ chmixer_pspd->port_idx = ucontrol->value.integer.value[4];
+
+ if (chmixer_pspd->enable) {
+ if (session_type == SESSION_TYPE_RX &&
+ !chmixer_pspd->override_in_ch_map) {
+ if (pdata->ch_map[fe_id] &&
+ pdata->ch_map[fe_id]->set_ch_map) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ } else {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->input_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = asm_ch_map[i];
+ }
+ chmixer_pspd->override_in_ch_map = true;
+ reset_override_in_ch_map = true;
+ } else if (session_type == SESSION_TYPE_TX &&
+ !chmixer_pspd->override_out_ch_map) {
+ if (pdata->ch_map[fe_id] &&
+ pdata->ch_map[fe_id]->set_ch_map) {
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] =
+ pdata->ch_map[fe_id]->channel_map[i];
+ } else {
+ q6asm_map_channels(asm_ch_map,
+ chmixer_pspd->output_channel, false);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] = asm_ch_map[i];
+ }
+ chmixer_pspd->override_out_ch_map = true;
+ reset_override_out_ch_map = true;
+ }
+ } else {
+ chmixer_pspd->override_out_ch_map = false;
+ chmixer_pspd->override_in_ch_map = false;
+ }
+
+ /* cache value and take effect during adm_open stage */
+ msm_pcm_routing_set_channel_mixer_cfg(fe_id,
+ session_type,
+ chmixer_pspd);
+
+ if (chmixer_pspd->enable && substream->runtime) {
+ prtd = substream->runtime->private_data;
+ if (!prtd) {
+ pr_err("%s find invalid prtd fail\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (prtd->audio_client) {
+ stream_id = prtd->audio_client->session;
+ be_id = chmixer_pspd->port_idx;
+ msm_pcm_routing_set_channel_mixer_runtime(be_id,
+ stream_id,
+ session_type,
+ chmixer_pspd);
+ }
+ }
+
+ if (reset_override_out_ch_map)
+ chmixer_pspd->override_out_ch_map = false;
+ if (reset_override_in_ch_map)
+ chmixer_pspd->override_in_ch_map = false;
+
+done:
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ ucontrol->value.integer.value[0] = chmixer_pspd->enable;
+ ucontrol->value.integer.value[1] = chmixer_pspd->rule;
+ ucontrol->value.integer.value[2] = chmixer_pspd->input_channel;
+ ucontrol->value.integer.value[3] = chmixer_pspd->output_channel;
+ ucontrol->value.integer.value[4] = chmixer_pspd->port_idx;
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_out_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->out_ch_map[i] =
+ ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_output_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->out_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ chmixer_pspd->override_in_ch_map = true;
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->in_ch_map[i] = ucontrol->value.integer.value[i];
+
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_input_map_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->in_ch_map[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_put(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ int i = 0;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ chmixer_pspd->channel_weight[channel][i] =
+ ucontrol->value.integer.value[i];
+ return 0;
+}
+
+static int msm_pcm_channel_mixer_weight_ctl_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value & 0xFF;
+ int session_type = (kcontrol->private_value >> 8) & 0xFF;
+ int channel = (kcontrol->private_value >> 16) & 0xFF;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct msm_plat_data *pdata = dev_get_drvdata(component->dev);
+ int i = 0;
+ struct msm_pcm_channel_mixer *chmixer_pspd;
+
+ if (channel <= 0 || channel > PCM_FORMAT_MAX_NUM_CHANNEL_V8) {
+ pr_err("%s: invalid channel number %d\n", __func__, channel);
+ return -EINVAL;
+ }
+ channel--;
+
+ chmixer_pspd = msm_pcm_get_chmixer(pdata, fe_id, session_type);
+ if (!chmixer_pspd) {
+ pr_err("%s: invalid chmixer_pspd in pdata", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ chmixer_pspd->channel_weight[channel][i];
+ return 0;
+}
+
+static int msm_pcm_add_platform_controls(struct snd_kcontrol_new *kctl,
+ struct snd_soc_pcm_runtime *rtd, const char *name_prefix,
+ const char *name_suffix, int session_type, int channels)
+{
+ int ret = -EINVAL;
+ char *mixer_name = NULL;
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *deviceNo = "NN";
+ const char *channelNo = "NN";
+ int ctl_len = 0;
+ struct snd_soc_component *component = NULL;
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ ctl_len = strlen(name_prefix) + 1 + strlen(deviceNo) + 1 +
+ strlen(channelNo) + 1 + strlen(name_suffix) + 1;
+
+ mixer_name = kzalloc(ctl_len, GFP_KERNEL);
+ if (mixer_name == NULL)
+ return -ENOMEM;
+
+ if (channels >= 0) {
+ snprintf(mixer_name, ctl_len, "%s %d %s %d",
+ name_prefix, pcm->device, name_suffix, channels);
+ kctl->private_value = (rtd->dai_link->id) | (session_type << 8) |
+ (channels << 16);
+ } else {
+ snprintf(mixer_name, ctl_len, "%s %d %s",
+ name_prefix, pcm->device, name_suffix);
+ kctl->private_value = (rtd->dai_link->id) | (session_type << 8);
+ }
+
+ kctl->name = mixer_name;
+ ret = snd_soc_add_component_controls(component, kctl, 1);
+ kfree(mixer_name);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_output_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_output_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Output Map";
+ int session_type = 0, ret = 0, channel = -1;
+ struct snd_kcontrol_new channel_mixer_output_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_output_map_info,
+ .put = msm_pcm_channel_mixer_output_map_ctl_put,
+ .get = msm_pcm_channel_mixer_output_map_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_output_map_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_input_map_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid channel map value ranges from 1 to 64 */
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 64;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_input_map_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Input Map";
+ int session_type = 0, ret = 0, channel = -1;
+ struct snd_kcontrol_new channel_mixer_input_map_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_input_map_info,
+ .put = msm_pcm_channel_mixer_input_map_ctl_put,
+ .get = msm_pcm_channel_mixer_input_map_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_input_map_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_cfg_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* five int values: enable, rule, in_channels, out_channels and port_id */
+ uinfo->count = 5;
+ /* Valid range is all positive values to support above controls */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = INT_MAX;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_cfg_controls(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Cfg";
+ int session_type = 0, ret = 0, channel = -1;
+ struct msm_plat_data *pdata = NULL;
+ struct snd_soc_component *component = NULL;
+ struct snd_kcontrol_new channel_mixer_cfg_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_cfg_info,
+ .put = msm_pcm_channel_mixer_cfg_ctl_put,
+ .get = msm_pcm_channel_mixer_cfg_ctl_get,
+ .private_value = 0,
+ };
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(component->dev);
+
+ pdata->pcm_device[rtd->dai_link->id] = rtd->pcm;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_cfg_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_channel_mixer_weight_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = PCM_FORMAT_MAX_NUM_CHANNEL_V8;
+ /* Valid range: 0 to 0x4000(Unity) gain weightage */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0x4000;
+ return 0;
+}
+
+static int msm_pcm_add_channel_mixer_weight_controls(
+ struct snd_soc_pcm_runtime *rtd,
+ int channel)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ const char *playback_mixer_ctl_name = "AudStr";
+ const char *capture_mixer_ctl_name = "AudStr Capture";
+ const char *suffix = "ChMixer Weight Ch";
+ int session_type = 0, ret = 0;
+ struct snd_kcontrol_new channel_mixer_weight_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_pcm_channel_mixer_weight_info,
+ .put = msm_pcm_channel_mixer_weight_ctl_put,
+ .get = msm_pcm_channel_mixer_weight_ctl_get,
+ .private_value = 0,
+ };
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream != NULL) {
+ session_type = SESSION_TYPE_RX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+ rtd, playback_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream != NULL) {
+ session_type = SESSION_TYPE_TX;
+ ret = msm_pcm_add_platform_controls(&channel_mixer_weight_control,
+ rtd, capture_mixer_ctl_name, suffix, session_type, channel);
+ if (ret < 0)
+ goto fail;
+ }
+ return 0;
+
+fail:
+ pr_err("%s: failed add platform ctl, err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int msm_pcm_add_channel_mixer_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ int i, ret = 0;
+ struct snd_pcm *pcm = NULL;
+ struct msm_plat_data *pdata = NULL;
+ struct snd_soc_component *component = NULL;
+
+ if (!rtd || !rtd->pcm) {
+ pr_err("%s invalid rtd or pcm\n", __func__);
+ return -EINVAL;
+ }
+ pcm = rtd->pcm;
+
+ component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ pdata = (struct msm_plat_data *)
+ dev_get_drvdata(component->dev);
+ if (!pdata) {
+ pr_err("%s: platform data not populated\n", __func__);
+ return -EINVAL;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream &&
+ !pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] =
+ kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+ if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream &&
+ !pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] =
+ kzalloc(sizeof(struct msm_pcm_channel_mixer), GFP_KERNEL);
+ if (!pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ ret = msm_pcm_add_channel_mixer_cfg_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer cfg controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_pcm_add_channel_mixer_input_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer input map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ ret = msm_pcm_add_channel_mixer_output_map_controls(rtd);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer output map controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+
+ for (i = 1; i <= PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++) {
+ ret = msm_pcm_add_channel_mixer_weight_controls(rtd, i);
+ if (ret) {
+ pr_err("%s: pcm add channel mixer weight controls failed:%d\n",
+ __func__, ret);
+ goto fail;
+ }
+ }
+ return 0;
+
+fail:
+ kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX]);
+ kfree(pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX]);
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_RX] = NULL;
+ pdata->chmixer_pspd[rtd->dai_link->id][SESSION_TYPE_TX] = NULL;
+
+ return ret;
+}
+
static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
{
int ret = 0;
@@ -1850,6 +2541,10 @@
if (ret)
pr_err("%s: pcm add app type controls failed:%d\n",
__func__, ret);
+ ret = msm_pcm_add_channel_mixer_controls(rtd);
+ if (ret)
+ pr_err("%s: pcm add channel mixer controls failed:%d\n",
+ __func__, ret);
return ret;
}
@@ -1973,8 +2668,15 @@
static int msm_pcm_remove(struct platform_device *pdev)
{
struct msm_plat_data *pdata;
+ int i = 0;
pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata) {
+ for (i = 0; i < MSM_FRONTEND_DAI_MM_SIZE; i++) {
+ kfree(pdata->chmixer_pspd[i][SESSION_TYPE_RX]);
+ kfree(pdata->chmixer_pspd[i][SESSION_TYPE_TX]);
+ }
+ }
kfree(pdata);
snd_soc_unregister_component(&pdev->dev);
return 0;
diff --git a/asoc/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h
index 8699a48..62fcebd 100644
--- a/asoc/msm-pcm-q6-v2.h
+++ b/asoc/msm-pcm-q6-v2.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017, 2019 The Linux Foundation. All rights reserved.
*/
#ifndef _MSM_PCM_H
@@ -115,6 +115,8 @@
int perf_mode;
struct snd_pcm *pcm;
struct msm_pcm_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
+ struct snd_pcm *pcm_device[MSM_FRONTEND_DAI_MM_SIZE];
+ struct msm_pcm_channel_mixer *chmixer_pspd[MSM_FRONTEND_DAI_MM_SIZE][2];
};
struct msm_pcm_ch_map {
diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c
index c49a757..dc403a5 100644
--- a/asoc/msm-pcm-routing-v2.c
+++ b/asoc/msm-pcm-routing-v2.c
@@ -84,6 +84,11 @@
#define WEIGHT_0_DB 0x4000
/* all the FEs which can support channel mixer */
static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE];
+
+/* all the FES which can support channel mixer for bidirection */
+static struct msm_pcm_channel_mixer
+ channel_mixer_v2[MSM_FRONTEND_DAI_MM_SIZE][2];
+
/* input BE for each FE */
static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS];
@@ -928,6 +933,51 @@
}
EXPORT_SYMBOL(msm_pcm_routing_send_chmix_cfg);
+/**
+ * msm_pcm_routing_set_channel_mixer_cfg - cache channel mixer
+ * setting before use case start.
+ *
+ * @fe_id: frontend idx
+ * @type: stream direction type
+ * @params: parameters of channel mixer setting
+ *
+ * Return 0 for success
+ */
+int msm_pcm_routing_set_channel_mixer_cfg(
+ int fe_id, int type,
+ struct msm_pcm_channel_mixer *params)
+{
+ int i, j = 0;
+
+ channel_mixer_v2[fe_id][type].enable = params->enable;
+ channel_mixer_v2[fe_id][type].rule = params->rule;
+ channel_mixer_v2[fe_id][type].input_channel =
+ params->input_channel;
+ channel_mixer_v2[fe_id][type].output_channel =
+ params->output_channel;
+ channel_mixer_v2[fe_id][type].port_idx = params->port_idx;
+
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ channel_mixer_v2[fe_id][type].in_ch_map[i] =
+ params->in_ch_map[i];
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ channel_mixer_v2[fe_id][type].out_ch_map[i] =
+ params->out_ch_map[i];
+
+ for (i = 0; i < ADM_MAX_CHANNELS; i++)
+ for (j = 0; j < ADM_MAX_CHANNELS; j++)
+ channel_mixer_v2[fe_id][type].channel_weight[i][j] =
+ params->channel_weight[i][j];
+
+ channel_mixer_v2[fe_id][type].override_in_ch_map =
+ params->override_in_ch_map;
+ channel_mixer_v2[fe_id][type].override_out_ch_map =
+ params->override_out_ch_map;
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_cfg);
+
int msm_pcm_routing_reg_stream_app_type_cfg(
int fedai_id, int session_type, int be_id,
struct msm_pcm_stream_app_type_cfg *cfg_data)
@@ -1490,6 +1540,60 @@
return session_id;
}
+static int msm_pcm_routing_channel_mixer_v2(int fe_id, bool perf_mode,
+ int dspst_id, int stream_type)
+{
+ int copp_idx = 0;
+ int sess_type = 0;
+ int j = 0, be_id = 0;
+ int ret = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
+ pr_err("%s: invalid FE %d\n", __func__, fe_id);
+ return 0;
+ }
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+ sess_type = SESSION_TYPE_RX;
+ else
+ sess_type = SESSION_TYPE_TX;
+
+ if (!(channel_mixer_v2[fe_id][sess_type].enable)) {
+ pr_debug("%s: channel mixer not enabled for FE %d direction %d\n",
+ __func__, fe_id, sess_type);
+ return 0;
+ }
+
+ be_id = channel_mixer_v2[fe_id][sess_type].port_idx - 1;
+ channel_mixer_v2[fe_id][sess_type].input_channels[0] =
+ channel_mixer_v2[fe_id][sess_type].input_channel;
+
+ pr_debug("%s sess type %d,fe_id %d,override in:%d out:%d,be active %d\n",
+ __func__, sess_type, fe_id,
+ channel_mixer_v2[fe_id][sess_type].override_in_ch_map,
+ channel_mixer_v2[fe_id][sess_type].override_out_ch_map,
+ msm_bedais[be_id].active);
+
+ if ((msm_bedais[be_id].active) &&
+ test_bit(fe_id, &msm_bedais[be_id].fe_sessions[0])) {
+ unsigned long copp =
+ session_copp_map[fe_id][sess_type][be_id];
+ for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+ if (test_bit(j, &copp)) {
+ copp_idx = j;
+ break;
+ }
+ }
+
+ ret = adm_programable_channel_mixer(
+ msm_bedais[be_id].port_id,
+ copp_idx, dspst_id, sess_type,
+ &channel_mixer_v2[fe_id][sess_type], 0);
+ }
+
+ return ret;
+}
+
static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
int dspst_id, int stream_type)
{
@@ -1498,6 +1602,14 @@
int i = 0, j = 0, be_id;
int ret = 0;
+ ret = msm_pcm_routing_channel_mixer_v2(fe_id, perf_mode,
+ dspst_id, stream_type);
+ if (ret) {
+ pr_err("%s channel mixer v2 cmd set failure%d\n", __func__,
+ fe_id);
+ return ret;
+ }
+
if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) {
pr_err("%s: invalid FE %d\n", __func__, fe_id);
return 0;
@@ -1537,7 +1649,7 @@
fe_id, be_id, msm_bedais[be_id].channel,
copp_idx);
ret = adm_programable_channel_mixer(
- msm_bedais[be_id].port_id,
+ get_port_id(msm_bedais[be_id].port_id),
copp_idx, dspst_id, sess_type,
channel_mixer + fe_id, i);
}
@@ -1546,6 +1658,72 @@
return ret;
}
+/**
+ * msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer
+ * setting during runtime.
+ *
+ * @be_id: backend index
+ * @session_id: session index
+ * @session_type: session type
+ * @params: parameters for channel mixer
+ *
+ * Retuen: 0 for success, else error
+ */
+int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *params)
+{
+ int rc = 0;
+ int port_id, copp_idx = 0;
+
+ be_id--;
+ if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: invalid backend id %d\n", __func__,
+ be_id);
+ return -EINVAL;
+ }
+
+ port_id = msm_bedais[be_id].port_id;
+ copp_idx = adm_get_default_copp_idx(port_id);
+ pr_debug("%s: port_id - %d, copp_idx %d session id - %d\n",
+ __func__, port_id, copp_idx, session_id);
+
+ if ((params->input_channel < 0) ||
+ (params->input_channel > ADM_MAX_CHANNELS)) {
+ pr_err("%s: invalid input channel %d\n", __func__,
+ params->input_channel);
+ return -EINVAL;
+ }
+
+ if ((params->output_channel < 0) ||
+ (params->output_channel > ADM_MAX_CHANNELS)) {
+ pr_err("%s: invalid output channel %d\n", __func__,
+ params->output_channel);
+ return -EINVAL;
+ }
+
+ params->input_channels[0] = params->input_channel;
+
+ pr_debug("%s sess type %d,override in:%d out:%d,be active %d\n",
+ __func__, session_type,
+ params->override_in_ch_map,
+ params->override_out_ch_map,
+ msm_bedais[be_id].active);
+
+ rc = adm_programable_channel_mixer(port_id,
+ copp_idx,
+ session_id,
+ session_type,
+ params,
+ 0);
+ if (rc) {
+ pr_err("%s: send params failed rc=%d\n", __func__, rc);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_pcm_routing_set_channel_mixer_runtime);
+
int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
int dspst_id, int stream_type)
{
@@ -3104,20 +3282,21 @@
"QUIN_TDM_RX_2", "QUIN_TDM_TX_2", "QUIN_TDM_RX_3", "QUIN_TDM_TX_3",
"QUIN_TDM_RX_4", "QUIN_TDM_TX_4", "QUIN_TDM_RX_5", "QUIN_TDM_TX_5",
"QUIN_TDM_RX_6", "QUIN_TDM_TX_6", "QUIN_TDM_RX_7", "QUIN_TDM_TX_7",
-"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", "DISPLAY_PORT_RX1",
-"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX",
-"QUIN_AUXPCM_RX", "QUIN_AUXPCM_TX",
-"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX",
-"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX",
-"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX",
-"INT6_MI2S_RX", "INT6_MI2S_TX", "WSA_CDC_DMA_RX_0",
-"WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_RX_1", "WSA_CDC_DMA_TX_1",
+"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX",
+"DISPLAY_PORT_RX1", "TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX",
+"QUAT_AUXPCM_TX", "QUIN_AUXPCM_RX", "QUIN_AUXPCM_TX", "INT0_MI2S_RX",
+"INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", "INT2_MI2S_RX",
+"INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", "INT4_MI2S_RX",
+"INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", "INT6_MI2S_RX",
+"INT6_MI2S_TX", "SEN_AUXPCM_RX", "SEN_AUXPCM_TX", "SENARY_MI2S_RX",
+"WSA_CDC_DMA_RX_0", "WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_RX_1","WSA_CDC_DMA_TX_1",
"WSA_CDC_DMA_TX_2", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_1", "VA_CDC_DMA_TX_2",
"RX_CDC_DMA_RX_0", "TX_CDC_DMA_TX_0", "RX_CDC_DMA_RX_1", "TX_CDC_DMA_TX_1",
"RX_CDC_DMA_RX_2", "TX_CDC_DMA_TX_2", "RX_CDC_DMA_RX_3", "TX_CDC_DMA_TX_3",
"RX_CDC_DMA_RX_4", "TX_CDC_DMA_TX_4", "RX_CDC_DMA_RX_5", "TX_CDC_DMA_TX_5",
"RX_CDC_DMA_RX_6", "RX_CDC_DMA_RX_7",
"PRI_SPDIF_TX", "SEC_SPDIF_RX", "SEC_SPDIF_TX",
+"SLIM_9_RX", "SLIM_9_TX", "AFE_LOOPBACK_TX"
};
static SOC_ENUM_SINGLE_DECL(mm1_channel_mux,
@@ -9373,6 +9552,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("AUX_PCM_UL_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_AUXPCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -9791,6 +9974,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_INT_FM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
@@ -9990,6 +10177,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
@@ -10185,6 +10376,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("INTERNAL_FM_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_INT_FM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
@@ -10412,6 +10607,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
@@ -10607,6 +10806,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
@@ -10814,6 +11017,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
@@ -11027,6 +11234,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
@@ -11170,6 +11381,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
@@ -11346,6 +11561,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
@@ -11455,6 +11674,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
@@ -11568,6 +11791,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
@@ -11596,6 +11823,10 @@
MSM_BACKEND_DAI_INCALL_RECORD_TX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
@@ -11991,6 +12222,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("SLIM_6_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_SLIMBUS_6_TX,
MSM_FRONTEND_DAI_MULTIMEDIA27, 1, 0, msm_routing_get_audio_mixer,
@@ -12042,6 +12277,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
@@ -12070,6 +12309,10 @@
MSM_BACKEND_DAI_INCALL_RECORD_TX,
MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
@@ -12151,6 +12394,10 @@
MSM_BACKEND_DAI_SLIMBUS_0_TX,
MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SLIM_1_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("PRI_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_PRI_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
@@ -12179,6 +12426,10 @@
MSM_BACKEND_DAI_INCALL_RECORD_TX,
MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_DOUBLE_EXT("SEC_MI2S_TX", SND_SOC_NOPM,
+ MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
SOC_DOUBLE_EXT("TERT_MI2S_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
@@ -20255,12 +20506,19 @@
{"MultiMedia8 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia8 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"},
{"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia4 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia17 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia18 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia19 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia28 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia28 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia29 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia29 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia8 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia2 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia4 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
@@ -20270,6 +20528,9 @@
{"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia18 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia19 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia28 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+ {"MultiMedia29 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
{"MultiMedia17 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia18 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia19 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -20282,16 +20543,26 @@
{"MultiMedia29 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
{"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia3 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia5 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia10 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia10 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia16 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"MultiMedia5 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"},
{"MultiMedia10 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia10 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"},
{"MultiMedia18 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"},
+ {"MultiMedia19 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"},
+ {"MultiMedia28 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"},
+ {"MultiMedia29 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"},
{"MultiMedia18 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"},
+ {"MultiMedia19 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"},
+ {"MultiMedia28 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"},
+ {"MultiMedia29 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"},
{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -20957,6 +21228,7 @@
{"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia1 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
{"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
{"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
@@ -20987,6 +21259,7 @@
{"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
{"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
{"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia6 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
{"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -21264,6 +21537,7 @@
{"MultiMedia8 Mixer", "PRI_SPDIF_TX", "PRI_SPDIF_TX"},
{"MultiMedia8 Mixer", "SEC_SPDIF_TX", "SEC_SPDIF_TX"},
+ {"MultiMedia9 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia9 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
{"MultiMedia9 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
{"MultiMedia9 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
@@ -21388,6 +21662,7 @@
{"MultiMedia21 Mixer", "AFE_LOOPBACK_TX", "AFE_LOOPBACK_TX"},
{"MultiMedia27 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia27 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
{"MultiMedia27 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
{"MultiMedia27 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia27 Mixer", "SLIM_9_TX", "SLIMBUS_9_TX"},
@@ -22786,11 +23061,14 @@
{"WSA_CDC_DMA_RX_0 Port Mixer", "VA_CDC_DMA_TX_0", "VA_CDC_DMA_TX_0"},
{"WSA_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"},
- {"RX_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"},
{"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
- {"RX_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"WSA_CDC_DMA_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0 Port Mixer"},
+
+ {"RX_CDC_DMA_RX_0 Port Mixer", "TX_CDC_DMA_TX_3", "TX_CDC_DMA_TX_3"},
+ {"RX_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"RX_CDC_DMA_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+ {"RX_CDC_DMA_RX_0", NULL, "RX_CDC_DMA_RX_0 Port Mixer"},
{"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h
index 9b663ff..fc93059 100644
--- a/asoc/msm-pcm-routing-v2.h
+++ b/asoc/msm-pcm-routing-v2.h
@@ -4,6 +4,7 @@
#ifndef _MSM_PCM_ROUTING_H
#define _MSM_PCM_ROUTING_H
#include <dsp/apr_audio-v2.h>
+#include <dsp/q6adm-v2.h>
/*
* These names are used by HAL to specify the BE. If any changes are
@@ -594,4 +595,13 @@
int op_channel_cnt, int *ch_wght_coeff,
int session_type, int stream_type);
int msm_pcm_routing_get_pp_ch_cnt(int fe_id, int session_type);
+
+int msm_pcm_routing_set_channel_mixer_cfg(
+ int fe_id, int session_type,
+ struct msm_pcm_channel_mixer *params);
+
+int msm_pcm_routing_set_channel_mixer_runtime(
+ int be_id, int session_id,
+ int session_type,
+ struct msm_pcm_channel_mixer *params);
#endif /*_MSM_PCM_H*/
diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c
index 222a3a9..48a784e 100644
--- a/asoc/msm-qti-pp-config.c
+++ b/asoc/msm-qti-pp-config.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/err.h>
@@ -824,6 +824,32 @@
return 0;
}
+static int msm_qti_pp_get_channel_map_capture(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8] = {0};
+ int i;
+
+ adm_get_multi_ch_map(channel_map, ADM_PATH_LIVE_REC);
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ ucontrol->value.integer.value[i] =
+ (unsigned int) channel_map[i];
+ return 0;
+}
+
+static int msm_qti_pp_put_channel_map_capture(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL_V8];
+ int i;
+
+ for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL_V8; i++)
+ channel_map[i] = (char)(ucontrol->value.integer.value[i]);
+ adm_set_multi_ch_map(channel_map, ADM_PATH_LIVE_REC);
+
+ return 0;
+}
+
/* Audio Sphere functions */
static void msm_qti_pp_asphere_init_state(void)
@@ -1172,6 +1198,13 @@
}
event_data = (struct msm_adsp_event_data *)payload;
+ if (event_data->payload_len < sizeof(struct msm_adsp_event_data)) {
+ pr_err("%s: event_data size of %x is less than expected.\n",
+ __func__, event_data->payload_len);
+ ret = -EINVAL;
+ goto done;
+ }
+
kctl->info(kctl, &kctl_info);
if (event_data->payload_len >
@@ -1406,11 +1439,17 @@
};
static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
- SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 34,
+ SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 47,
0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_mixer,
msm_qti_pp_put_channel_map_mixer),
};
+static const struct snd_kcontrol_new multi_ch_channel_map_capture_controls[] = {
+ SOC_SINGLE_MULTI_EXT("Capture Device Channel Map", SND_SOC_NOPM, 0, 47,
+ 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_capture,
+ msm_qti_pp_put_channel_map_capture),
+};
+
static const struct snd_kcontrol_new get_rms_controls[] = {
SOC_SINGLE_EXT("Get RMS", SND_SOC_NOPM, 0, 0xFFFFFFFF,
@@ -1607,6 +1646,11 @@
snd_soc_add_component_controls(component, get_rms_controls,
ARRAY_SIZE(get_rms_controls));
+ snd_soc_add_component_controls(component,
+ multi_ch_channel_map_capture_controls,
+ ARRAY_SIZE(multi_ch_channel_map_capture_controls));
+
+
snd_soc_add_component_controls(component, eq_enable_mixer_controls,
ARRAY_SIZE(eq_enable_mixer_controls));
diff --git a/asoc/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c
index 1b431ae..9980874 100644
--- a/asoc/msm-transcode-loopback-q6-v2.c
+++ b/asoc/msm-transcode-loopback-q6-v2.c
@@ -178,10 +178,11 @@
if (cstream->direction == SND_COMPRESS_CAPTURE) {
compr_cap.direction = SND_COMPRESS_CAPTURE;
- compr_cap.num_codecs = 3;
+ compr_cap.num_codecs = 4;
compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
compr_cap.codecs[1] = SND_AUDIOCODEC_AC3;
compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3;
+ compr_cap.codecs[3] = SND_AUDIOCODEC_TRUEHD;
memcpy(&trans->source_compr_cap, &compr_cap,
sizeof(struct snd_compr_caps));
}
@@ -482,6 +483,11 @@
trans->source.codec_format =
FORMAT_EAC3;
break;
+ case SND_AUDIOCODEC_TRUEHD:
+ pr_debug("Source SND_AUDIOCODEC_TRUEHD\n");
+ trans->source.codec_format =
+ FORMAT_TRUEHD;
+ break;
default:
pr_debug("%s: unknown source codec\n", __func__);
ret = -EINVAL;
diff --git a/asoc/qcs405.c b/asoc/qcs405.c
index 6e4596f..0bbe37b 100644
--- a/asoc/qcs405.c
+++ b/asoc/qcs405.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/delay.h>
@@ -179,6 +179,7 @@
struct device_node *dmic_23_gpio_p; /* used by pinctrl API */
struct device_node *dmic_45_gpio_p; /* used by pinctrl API */
struct device_node *dmic_67_gpio_p; /* used by pinctrl API */
+ struct device_node *lineout_booster_gpio_p; /* used by pinctrl API */
struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */
int dmic_01_gpio_cnt;
int dmic_23_gpio_cnt;
@@ -3838,6 +3839,30 @@
return 0;
}
+static int msm_lineout_booster_ctrl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct snd_soc_card *card = component->card;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(card);
+
+ pr_debug("%s: event = %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msm_cdc_pinctrl_select_active_state(
+ pdata->lineout_booster_gpio_p);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ msm_cdc_pinctrl_select_sleep_state(
+ pdata->lineout_booster_gpio_p);
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
@@ -3847,6 +3872,7 @@
SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0,
msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("lineout booster", msm_lineout_booster_ctrl_event),
SND_SOC_DAPM_MIC("Analog Mic3", NULL),
SND_SOC_DAPM_MIC("Analog Mic4", NULL),
@@ -5047,7 +5073,6 @@
return ret;
}
-
static int msm_snd_cdc_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -6483,8 +6508,8 @@
.codec_name = "snd-soc-dummy",
},
{
- .name = MSM_DAILINK_NAME(Compr Capture),
- .stream_name = "Compr Capture",
+ .name = MSM_DAILINK_NAME(Compr Capture2),
+ .stream_name = "Compr Capture2",
.cpu_dai_name = "MultiMedia18",
.platform_name = "msm-compress-dsp",
.dynamic = 1,
@@ -6527,6 +6552,48 @@
.ignore_pmdown_time = 1,
.id = MSM_FRONTEND_DAI_MULTIMEDIA27,
},
+ {
+ .name = MSM_DAILINK_NAME(Compr Capture3),
+ .stream_name = "Compr Capture3",
+ .cpu_dai_name = "MultiMedia19",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_pmdown_time = 1,
+ .id = MSM_FRONTEND_DAI_MULTIMEDIA19,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compr Capture4),
+ .stream_name = "Compr Capture4",
+ .cpu_dai_name = "MultiMedia28",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_pmdown_time = 1,
+ .id = MSM_FRONTEND_DAI_MULTIMEDIA28,
+ },
+ {
+ .name = MSM_DAILINK_NAME(Compr Capture5),
+ .stream_name = "Compr Capture5",
+ .cpu_dai_name = "MultiMedia29",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_pmdown_time = 1,
+ .id = MSM_FRONTEND_DAI_MULTIMEDIA29,
+ },
};
static struct snd_soc_dai_link msm_common_be_dai_links[] = {
@@ -8603,6 +8670,8 @@
"qcom,cdc-dmic45-gpios", 0);
pdata->dmic_67_gpio_p = of_parse_phandle(pdev->dev.of_node,
"qcom,cdc-dmic67-gpios", 0);
+ pdata->lineout_booster_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,lineout-booster-gpio", 0);
pdata->mi2s_gpio_p[PRIM_MI2S] = of_parse_phandle(pdev->dev.of_node,
"qcom,pri-mi2s-gpios", 0);
diff --git a/asoc/sm6150.c b/asoc/sm6150.c
index 55fe178..84e05c8 100644
--- a/asoc/sm6150.c
+++ b/asoc/sm6150.c
@@ -29,6 +29,7 @@
#include "msm-pcm-routing-v2.h"
#include <asoc/msm-cdc-pinctrl.h>
#include "codecs/wcd934x/wcd934x.h"
+#include "codecs/wcd9335.h"
#include "codecs/wcd934x/wcd934x-mbhc.h"
#include "codecs/wcd937x/wcd937x-mbhc.h"
#include "codecs/wsa881x.h"
@@ -204,6 +205,8 @@
struct snd_info_entry *codec_root;
int usbc_en2_gpio; /* used by gpio driver API */
struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */
+ int hph_en1_gpio;
+ int hph_en0_gpio;
struct device_node *dmic01_gpio_p; /* used by pinctrl API */
struct device_node *dmic23_gpio_p; /* used by pinctrl API */
struct device_node *us_euro_gpio_p; /* used by pinctrl API */
@@ -653,13 +656,14 @@
.key_code[6] = 0,
.key_code[7] = 0,
.linein_th = 5000,
- .moisture_en = true,
+ .moisture_en = false,
.mbhc_micbias = MIC_BIAS_2,
.anc_micbias = MIC_BIAS_2,
.enable_anc_mic_detect = false,
+ .moisture_duty_cycle_en = true,
};
-static struct snd_soc_dapm_route wcd_audio_paths_tavil[] = {
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
{"MIC BIAS1", NULL, "MCLK TX"},
{"MIC BIAS2", NULL, "MCLK TX"},
{"MIC BIAS3", NULL, "MCLK TX"},
@@ -3578,7 +3582,7 @@
cdc_dma_tx_sample_rate_put),
};
-static const struct snd_kcontrol_new msm_tavil_snd_controls[] = {
+static const struct snd_kcontrol_new msm_ext_snd_controls[] = {
SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
slim_rx_ch_get, slim_rx_ch_put),
SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
@@ -3867,6 +3871,8 @@
if (!strcmp(component->name, "tavil_codec")) {
ret = tavil_cdc_mclk_enable(component, enable);
+ } else if (!strcmp(dev_name(component->dev), "tasha_codec")) {
+ ret = tasha_cdc_mclk_enable(component, enable, dapm);
} else {
dev_err(component->dev, "%s: unknown codec to enable ext clk\n",
__func__);
@@ -3882,6 +3888,8 @@
if (!strcmp(component->name, "tavil_codec")) {
ret = tavil_cdc_mclk_tx_enable(component, enable);
+ } else if (!strcmp(dev_name(component->dev), "tasha_codec")) {
+ ret = tasha_cdc_mclk_tx_enable(component, enable, dapm);
} else {
dev_err(component->dev, "%s: unknown codec to enable TX ext clk\n",
__func__);
@@ -3961,7 +3969,7 @@
return 0;
}
-static const struct snd_soc_dapm_widget msm_dapm_widgets_tavil[] = {
+static const struct snd_soc_dapm_widget msm_ext_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm_mclk_event,
@@ -3972,12 +3980,19 @@
SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+ SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
SND_SOC_DAPM_MIC("Handset Mic", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Secondary Mic", NULL),
SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic4", NULL),
SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+ SND_SOC_DAPM_MIC("Analog Mic8", NULL),
SND_SOC_DAPM_MIC("Digital Mic0", NULL),
SND_SOC_DAPM_MIC("Digital Mic1", NULL),
@@ -3985,6 +4000,7 @@
SND_SOC_DAPM_MIC("Digital Mic3", NULL),
SND_SOC_DAPM_MIC("Digital Mic4", NULL),
SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
};
static int msm_dmic_event(struct snd_soc_dapm_widget *w,
@@ -4744,6 +4760,28 @@
afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
}
+static int msm_config_hph_en0_gpio(struct snd_soc_component *component, bool high)
+{
+ struct snd_soc_card *card = component->card;
+ struct msm_asoc_mach_data *pdata;
+ int val;
+
+ if (!card)
+ return 0;
+
+ pdata = snd_soc_card_get_drvdata(card);
+ if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio))
+ return 0;
+
+ val = gpio_get_value_cansleep(pdata->hph_en0_gpio);
+ if ((!!val) == high)
+ return 0;
+
+ gpio_direction_output(pdata->hph_en0_gpio, (int)high);
+
+ return 1;
+}
+
static int msm_audrx_tavil_init(struct snd_soc_pcm_runtime *rtd)
{
int ret = 0;
@@ -4781,8 +4819,8 @@
}
dapm = snd_soc_component_get_dapm(component);
- ret = snd_soc_add_component_controls(component, msm_tavil_snd_controls,
- ARRAY_SIZE(msm_tavil_snd_controls));
+ ret = snd_soc_add_component_controls(component, msm_ext_snd_controls,
+ ARRAY_SIZE(msm_ext_snd_controls));
if (ret < 0) {
pr_err("%s: add_codec_controls failed, err %d\n",
__func__, ret);
@@ -4797,11 +4835,11 @@
return ret;
}
- snd_soc_dapm_new_controls(dapm, msm_dapm_widgets_tavil,
- ARRAY_SIZE(msm_dapm_widgets_tavil));
+ snd_soc_dapm_new_controls(dapm, msm_ext_dapm_widgets,
+ ARRAY_SIZE(msm_ext_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tavil,
- ARRAY_SIZE(wcd_audio_paths_tavil));
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+ ARRAY_SIZE(wcd_audio_paths));
snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
@@ -4875,16 +4913,197 @@
}
card = rtd->card->snd_card;
- entry = snd_info_create_subdir(card->module, "codecs",
- card->proc_root);
- if (!entry) {
- pr_debug("%s: Cannot create codecs module entry\n",
- __func__);
- ret = 0;
+ if (!pdata->codec_root) {
+ entry = snd_info_create_subdir(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ ret = 0;
+ goto err;
+ }
+ pdata->codec_root = entry;
+ }
+ tavil_codec_info_create_codec_entry(pdata->codec_root, component);
+
+ codec_reg_done = true;
+ return 0;
+err:
+ return ret;
+}
+
+static int msm_audrx_tasha_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret = 0;
+ void *config_data;
+ struct snd_soc_component *component = NULL;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_component *aux_comp;
+ struct snd_card *card;
+ struct snd_info_entry *entry;
+ struct msm_asoc_mach_data *pdata =
+ snd_soc_card_get_drvdata(rtd->card);
+
+ /* Codec SLIMBUS configuration
+ * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+ * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+ * TX14, TX15, TX16
+ */
+ unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156};
+ unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143};
+
+ pr_info("%s: dev_name:%s\n", __func__, dev_name(cpu_dai->dev));
+
+ rtd->pmdown_time = 0;
+
+ component = snd_soc_rtdcom_lookup(rtd, "tasha_codec");
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ dapm = snd_soc_component_get_dapm(component);
+
+ ret = snd_soc_add_component_controls(component, msm_ext_snd_controls,
+ ARRAY_SIZE(msm_ext_snd_controls));
+ if (ret < 0) {
+ pr_err("%s: add_component_controls failed, err %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_component_controls(component, msm_common_snd_controls,
+ ARRAY_SIZE(msm_common_snd_controls));
+ if (ret < 0) {
+ pr_err("%s: add_component_controls failed, err %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(dapm, msm_ext_dapm_widgets,
+ ARRAY_SIZE(msm_ext_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+ ARRAY_SIZE(wcd_audio_paths));
+
+ snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+ snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+ snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+ snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7");
+ snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8");
+
+ snd_soc_dapm_ignore_suspend(dapm, "EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC0");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+ snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+ snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+
+ snd_soc_dapm_sync(dapm);
+
+ snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+ tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+ msm_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+
+ ret = msm_afe_set_config(component);
+ if (ret) {
+ pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
goto err;
}
- pdata->codec_root = entry;
- tavil_codec_info_create_codec_entry(pdata->codec_root, component);
+ pdata->is_afe_config_done = true;
+
+ config_data = msm_codec_fn.get_afe_config_fn(component,
+ AFE_AANC_VERSION);
+ if (config_data) {
+ ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+ if (ret) {
+ pr_err("%s: Failed to set aanc version %d\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Default configuration is for WSA8815.
+ */
+ pr_debug("%s: Number of aux devices: %d\n",
+ __func__, rtd->card->num_aux_devs);
+ if (rtd->card->num_aux_devs &&
+ !list_empty(&rtd->card->aux_comp_list)) {
+ aux_comp = list_first_entry(&rtd->card->aux_comp_list,
+ struct snd_soc_component, card_aux_list);
+ if (!strcmp(aux_comp->name, WSA8810_NAME_1) ||
+ !strcmp(aux_comp->name, WSA8810_NAME_2)) {
+ tasha_set_spkr_mode(component, SPKR_MODE_1);
+ tasha_set_spkr_gain_offset(component,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
+ }
+
+ card = rtd->card->snd_card;
+ if (!pdata->codec_root) {
+ entry = snd_info_create_subdir(card->module, "codecs",
+ card->proc_root);
+ if (!entry) {
+ pr_debug("%s: Cannot create codecs module entry\n",
+ __func__);
+ ret = 0;
+ goto err;
+ }
+ pdata->codec_root = entry;
+ }
+ tasha_codec_info_create_codec_entry(pdata->codec_root, component);
+ tasha_mbhc_zdet_gpio_ctrl(msm_config_hph_en0_gpio, component);
codec_reg_done = true;
return 0;
@@ -5297,6 +5516,48 @@
return ret;
}
+int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai_link *dai_link = rtd->dai_link;
+ int ret = 0;
+ u32 tx_ch[SLIM_MAX_TX_PORTS];
+ u32 tx_ch_cnt = 0;
+
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+ pr_err("%s: Invalid stream type %d\n",
+ __func__, substream->stream);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+ codec_dai->name, codec_dai->id);
+ ret = snd_soc_dai_get_channel_map(codec_dai,
+ &tx_ch_cnt, tx_ch, NULL, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to get codec chan map\n, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ pr_debug("%s: tx_ch_cnt(%d) id %d\n",
+ __func__, tx_ch_cnt, dai_link->id);
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ tx_ch_cnt, tx_ch, 0, 0);
+ if (ret < 0) {
+ pr_err("%s: failed to set cpu chan map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+end:
+ return ret;
+}
+
static int msm_get_port_id(int be_id)
{
int afe_port_id;
@@ -5780,6 +6041,9 @@
.hw_params = msm_wcn_hw_params,
};
+static struct snd_soc_ops msm_ext_cpe_ops = {
+ .hw_params = msm_snd_cpe_hw_params,
+};
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link msm_common_dai_links[] = {
@@ -6382,7 +6646,6 @@
},
};
-
static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = {
{/* hw:x,37 */
.name = LPASS_BE_SLIMBUS_4_TX,
@@ -6513,6 +6776,96 @@
},
};
+static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = {
+ /* tasha_vifeedback for speaker protection */
+ {
+ .name = LPASS_BE_SLIMBUS_4_TX,
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_vifeedback",
+ .id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ /* Ultrasound RX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Playback",
+ .stream_name = "SLIMBUS_2 Hostless Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16388",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx2",
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ignore_pmdown_time = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+ /* Ultrasound TX DAI Link */
+ {
+ .name = "SLIMBUS_2 Hostless Capture",
+ .stream_name = "SLIMBUS_2 Hostless Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16389",
+ .platform_name = "msm-pcm-hostless",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx2",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ops = &msm_slimbus_2_be_ops,
+ },
+ /* CPE LSM direct dai-link */
+ {
+ .name = "CPE Listen service",
+ .stream_name = "CPE Listen Audio Service",
+ .cpu_dai_name = "msm-dai-slim",
+ .platform_name = "msm-cpe-lsm",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "tasha_mad1",
+ .codec_name = "tasha_codec",
+ .ops = &msm_ext_cpe_ops,
+ },
+ {
+ .name = "SLIMBUS_6 Hostless Playback",
+ .stream_name = "SLIMBUS_6 Hostless",
+ .cpu_dai_name = "SLIMBUS6_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
+};
+
static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = {
{
.name = MSM_DAILINK_NAME(ASM Loopback),
@@ -6998,6 +7351,165 @@
},
};
+static struct snd_soc_dai_link msm_tasha_be_dai_links[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_tasha_init,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_RX,
+ .stream_name = "Slimbus1 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16386",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_1_TX,
+ .stream_name = "Slimbus1 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16387",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx3",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_RX,
+ .stream_name = "Slimbus3 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16390",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_3_TX,
+ .stream_name = "Slimbus3 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16391",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_4_RX,
+ .stream_name = "Slimbus4 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16392",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mix_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_5_RX,
+ .stream_name = "Slimbus5 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16394",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx3",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_mad1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_6_RX,
+ .stream_name = "Slimbus6 Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.16396",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tasha_codec",
+ .codec_dai_name = "tasha_rx4",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ /* dai link has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
{
.name = LPASS_BE_SLIMBUS_7_RX,
@@ -7519,10 +8031,12 @@
ARRAY_SIZE(msm_common_dai_links) +
ARRAY_SIZE(msm_tavil_fe_dai_links) +
ARRAY_SIZE(msm_bolero_fe_dai_links) +
+ ARRAY_SIZE(msm_tasha_fe_dai_links) +
ARRAY_SIZE(msm_common_misc_fe_dai_links) +
ARRAY_SIZE(msm_int_compress_capture_dai) +
ARRAY_SIZE(msm_common_be_dai_links) +
ARRAY_SIZE(msm_tavil_be_dai_links) +
+ ARRAY_SIZE(msm_tasha_be_dai_links) +
ARRAY_SIZE(msm_wcn_be_dai_links) +
ARRAY_SIZE(ext_disp_be_dai_link) +
ARRAY_SIZE(msm_mi2s_be_dai_links) +
@@ -7574,6 +8088,50 @@
return ret;
}
+static int msm_snd_card_tasha_late_probe(struct snd_soc_card *card)
+{
+ const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_component *component;
+ int ret = 0;
+ void *mbhc_calibration;
+
+ rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+ if (!rtd) {
+ dev_err(card->dev,
+ "%s: snd_soc_get_pcm_runtime for %s failed!\n",
+ __func__, be_dl_name);
+ ret = -EINVAL;
+ goto err_pcm_runtime;
+ }
+
+ component = snd_soc_rtdcom_lookup(rtd, "tasha_codec");
+ if (!component) {
+ pr_err("%s: component is NULL\n", __func__);
+ ret = -EINVAL;
+ goto err_pcm_runtime;
+ }
+
+ mbhc_calibration = def_wcd_mbhc_cal();
+ if (!mbhc_calibration) {
+ ret = -ENOMEM;
+ goto err_mbhc_cal;
+ }
+ wcd_mbhc_cfg.calibration = mbhc_calibration;
+ ret = tasha_mbhc_hs_detect(component, &wcd_mbhc_cfg);
+ if (ret) {
+ dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+ __func__, ret);
+ goto err_hs_detect;
+ }
+ return 0;
+
+err_hs_detect:
+ kfree(mbhc_calibration);
+err_mbhc_cal:
+err_pcm_runtime:
+ return ret;
+}
static int msm_populate_dai_link_component_of_node(
struct snd_soc_card *card)
@@ -7667,8 +8225,8 @@
struct snd_soc_component *component =
snd_soc_rtdcom_lookup(rtd, "msm-stub-codec");
- ret = snd_soc_add_component_controls(component, msm_tavil_snd_controls,
- ARRAY_SIZE(msm_tavil_snd_controls));
+ ret = snd_soc_add_component_controls(component, msm_ext_snd_controls,
+ ARRAY_SIZE(msm_ext_snd_controls));
if (ret < 0) {
dev_err(component->dev,
"%s: add_codec_controls failed, err = %d\n",
@@ -7798,6 +8356,7 @@
u32 mi2s_audio_intf = 0, ext_disp_audio_intf = 0;
u32 wcn_btfm_intf = 0;
const struct of_device_id *match;
+ u32 tasha_codec = 0;
match = of_match_node(sm6150_asoc_machine_of_match, dev->of_node);
if (!match) {
@@ -7822,22 +8381,33 @@
rc = of_property_read_u32(dev->of_node, "qcom,tavil_codec",
&tavil_codec);
- if (rc) {
+ if (rc)
dev_dbg(dev, "%s: No DT match for tavil codec\n",
__func__);
- } else {
- if (tavil_codec) {
- card->late_probe =
- msm_snd_card_tavil_late_probe;
- memcpy(msm_sm6150_dai_links + total_links,
- msm_tavil_fe_dai_links,
- sizeof(msm_tavil_fe_dai_links));
- total_links +=
- ARRAY_SIZE(msm_tavil_fe_dai_links);
- }
- }
- if (!tavil_codec) {
+ rc = of_property_read_u32(dev->of_node, "qcom,tasha_codec",
+ &tasha_codec);
+ if (rc)
+ dev_dbg(dev, "%s: No DT match for tasha codec\n",
+ __func__);
+
+ if (tavil_codec) {
+ card->late_probe =
+ msm_snd_card_tavil_late_probe;
+ memcpy(msm_sm6150_dai_links + total_links,
+ msm_tavil_fe_dai_links,
+ sizeof(msm_tavil_fe_dai_links));
+ total_links +=
+ ARRAY_SIZE(msm_tavil_fe_dai_links);
+ } else if (tasha_codec) {
+ card->late_probe =
+ msm_snd_card_tasha_late_probe;
+ memcpy(msm_sm6150_dai_links + total_links,
+ msm_tasha_fe_dai_links,
+ sizeof(msm_tasha_fe_dai_links));
+ total_links +=
+ ARRAY_SIZE(msm_tasha_fe_dai_links);
+ } else {
memcpy(msm_sm6150_dai_links + total_links,
msm_bolero_fe_dai_links,
sizeof(msm_bolero_fe_dai_links));
@@ -7862,6 +8432,11 @@
msm_tavil_be_dai_links,
sizeof(msm_tavil_be_dai_links));
total_links += ARRAY_SIZE(msm_tavil_be_dai_links);
+ } else if (tasha_codec) {
+ memcpy(msm_sm6150_dai_links + total_links,
+ msm_tasha_be_dai_links,
+ sizeof(msm_tasha_be_dai_links));
+ total_links += ARRAY_SIZE(msm_tasha_be_dai_links);
} else {
memcpy(msm_sm6150_dai_links + total_links,
msm_wsa_cdc_dma_be_dai_links,
@@ -8196,7 +8771,8 @@
__func__, found);
codec_aux_dev:
- if (strcmp(card->name, "sm6150-tavil-snd-card")) {
+ if (!strnstr(card->name, "tavil", strlen(card->name)) &&
+ !strnstr(card->name, "tasha", strlen(card->name))) {
/* Get maximum aux codec device count for this platform */
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,codec-max-aux-devs",
@@ -8431,7 +9007,8 @@
goto err;
}
- if (!strcmp(card->name, "sm6150-tavil-snd-card")) {
+ if (strnstr(card->name, "tavil", strlen(card->name)) ||
+ strnstr(card->name, "tasha", strlen(card->name))) {
pdata = snd_soc_card_get_drvdata(card);
if (!pdata->is_afe_config_done) {
const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
@@ -8481,13 +9058,41 @@
dev_dbg(dev, "%s: setting snd_card to OFFLINE\n", __func__);
snd_soc_card_change_online_state(card, 0);
- if (!strcmp(card->name, "sm6150-tavil-snd-card")) {
+ if (strnstr(card->name, "tavil", strlen(card->name)) ||
+ strnstr(card->name, "tasha", strlen(card->name))) {
pdata = snd_soc_card_get_drvdata(card);
msm_afe_clear_config();
pdata->is_afe_config_done = false;
}
}
+static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata)
+{
+ int ret = 0;
+
+ if (gpio_is_valid(pdata->hph_en1_gpio)) {
+ pr_debug("%s: hph_en1_gpio request %d\n", __func__,
+ pdata->hph_en1_gpio);
+ ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio");
+ if (ret) {
+ pr_err("%s: hph_en1_gpio request failed, ret:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+ if (gpio_is_valid(pdata->hph_en0_gpio)) {
+ pr_debug("%s: hph_en0_gpio request %d\n", __func__,
+ pdata->hph_en0_gpio);
+ ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio");
+ if (ret)
+ pr_err("%s: hph_en0_gpio request failed, ret:%d\n",
+ __func__, ret);
+ }
+
+err:
+ return ret;
+}
+
static const struct snd_event_ops sm6150_ssr_ops = {
.enable = sm6150_ssr_enable,
.disable = sm6150_ssr_disable,
@@ -8589,20 +9194,31 @@
}
dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
- pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,hph-en1-gpio", 0);
- if (!pdata->hph_en1_gpio_p) {
- dev_dbg(&pdev->dev, "property %s not detected in node %s\n",
- "qcom,hph-en1-gpio",
- pdev->dev.of_node->full_name);
+ if (!gpio_is_valid(pdata->hph_en1_gpio))
+ pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en1-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en1-gpio", pdev->dev.of_node->full_name);
}
- pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node,
"qcom,hph-en0-gpio", 0);
- if (!pdata->hph_en0_gpio_p) {
- dev_dbg(&pdev->dev, "property %s not detected in node %s\n",
- "qcom,hph-en0-gpio",
- pdev->dev.of_node->full_name);
+ if (!gpio_is_valid(pdata->hph_en0_gpio))
+ pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+ "qcom,hph-en0-gpio", 0);
+ if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) {
+ dev_dbg(&pdev->dev, "property %s not detected in node %s",
+ "qcom,hph-en0-gpio", pdev->dev.of_node->full_name);
+ }
+
+ ret = msm_ext_prepare_hifi(pdata);
+ if (ret) {
+ dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n",
+ ret);
+ ret = 0;
}
ret = of_property_read_string(pdev->dev.of_node,
@@ -8669,7 +9285,8 @@
}
msm_i2s_auxpcm_init(pdev);
- if (strcmp(card->name, "sm6150-tavil-snd-card")) {
+ if (!strnstr(card->name, "tavil", strlen(card->name)) &&
+ !strnstr(card->name, "tasha", strlen(card->name))) {
pdata->dmic01_gpio_p = of_parse_phandle(pdev->dev.of_node,
"qcom,cdc-dmic01-gpios",
0);
diff --git a/asoc/sm8250-port-config.h b/asoc/sm8250-port-config.h
index a29f106..0a33c5f 100644
--- a/asoc/sm8250-port-config.h
+++ b/asoc/sm8250-port-config.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
#ifndef _SM8250_PORT_CONFIG
@@ -17,10 +17,10 @@
static struct port_params wsa_frame_params_default[SWR_MSTR_PORT_LEN] = {
{7, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{31, 2, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
- {63, 12, 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ {63, 12, 31, 0xFF, 0xFF, 0xFF, 0x1, 0xFF, 0xFF},
{7, 6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{31, 18, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
- {63, 13, 31, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ {63, 13, 31, 0xFF, 0xFF, 0xFF, 0x1, 0xFF, 0xFF},
{15, 7, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{15, 10, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
};
diff --git a/dsp/codecs/audio_aac.c b/dsp/codecs/audio_aac.c
index cf3d448..32106e5 100644
--- a/dsp/codecs/audio_aac.c
+++ b/dsp/codecs/audio_aac.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/msm_audio_aac.h>
@@ -383,7 +393,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_alac.c b/dsp/codecs/audio_alac.c
index ea05d71..f7e0932 100644
--- a/dsp/codecs/audio_alac.c
+++ b/dsp/codecs/audio_alac.c
@@ -1,6 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -290,7 +299,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_amrnb.c b/dsp/codecs/audio_amrnb.c
index efec4d2..ea6d02f 100644
--- a/dsp/codecs/audio_amrnb.c
+++ b/dsp/codecs/audio_amrnb.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -137,7 +147,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
@@ -178,7 +188,7 @@
if (IS_ERR(audio->dentry))
pr_debug("debugfs_create_file failed\n");
#endif
- pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__,
+ pr_info_ratelimited("%s:amrnb decoder open success, session_id = %d\n", __func__,
audio->ac->session);
return rc;
fail:
diff --git a/dsp/codecs/audio_amrwbplus.c b/dsp/codecs/audio_amrwbplus.c
index d06b054..6cadab7 100644
--- a/dsp/codecs/audio_amrwbplus.c
+++ b/dsp/codecs/audio_amrwbplus.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/msm_audio_amrwbplus.h>
#include <linux/compat.h>
@@ -313,7 +323,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_ape.c b/dsp/codecs/audio_ape.c
index 219df40..1e12981 100644
--- a/dsp/codecs/audio_ape.c
+++ b/dsp/codecs/audio_ape.c
@@ -1,5 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -271,7 +281,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_evrc.c b/dsp/codecs/audio_evrc.c
index c3bf9b8..48d2652 100644
--- a/dsp/codecs/audio_evrc.c
+++ b/dsp/codecs/audio_evrc.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include "audio_utils_aio.h"
@@ -94,7 +104,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_g711alaw.c b/dsp/codecs/audio_g711alaw.c
index 3dac27d..b4a1d7e 100644
--- a/dsp/codecs/audio_g711alaw.c
+++ b/dsp/codecs/audio_g711alaw.c
@@ -1,5 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -245,7 +255,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_g711mlaw.c b/dsp/codecs/audio_g711mlaw.c
index c0102d0..1cf8a97 100644
--- a/dsp/codecs/audio_g711mlaw.c
+++ b/dsp/codecs/audio_g711mlaw.c
@@ -1,5 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -244,7 +254,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_mp3.c b/dsp/codecs/audio_mp3.c
index 47446d3..7228cba 100644
--- a/dsp/codecs/audio_mp3.c
+++ b/dsp/codecs/audio_mp3.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include "audio_utils_aio.h"
@@ -96,7 +106,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c
index fc3f492..2c88ebf 100644
--- a/dsp/codecs/audio_multi_aac.c
+++ b/dsp/codecs/audio_multi_aac.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/msm_audio_aac.h>
@@ -437,7 +447,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_qcelp.c b/dsp/codecs/audio_qcelp.c
index fcf39f5..7e88cfd 100644
--- a/dsp/codecs/audio_qcelp.c
+++ b/dsp/codecs/audio_qcelp.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017, 2019 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include "audio_utils_aio.h"
@@ -101,7 +111,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c
index eabf170..f221635 100644
--- a/dsp/codecs/audio_utils_aio.c
+++ b/dsp/codecs/audio_utils_aio.c
@@ -1,7 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2019, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/module.h>
@@ -1335,7 +1345,7 @@
audio->drv_ops.in_flush = audio_aio_async_in_flush;
q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
} else {
- pr_err("%s[%pK]:SIO interface not supported\n",
+ pr_err_ratelimited("%s[%pK]:SIO interface not supported\n",
__func__, audio);
rc = -EACCES;
goto fail;
@@ -1548,7 +1558,7 @@
break;
}
default:
- pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+ pr_err_ratelimited("%s: Unknown ioctl cmd = %d", __func__, cmd);
rc = -EINVAL;
}
return rc;
diff --git a/dsp/codecs/audio_wma.c b/dsp/codecs/audio_wma.c
index cdd23ff..66c818d 100644
--- a/dsp/codecs/audio_wma.c
+++ b/dsp/codecs/audio_wma.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2019, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -253,7 +263,7 @@
}
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/codecs/audio_wmapro.c b/dsp/codecs/audio_wmapro.c
index c0a2757..4fc3312 100644
--- a/dsp/codecs/audio_wmapro.c
+++ b/dsp/codecs/audio_wmapro.c
@@ -3,7 +3,17 @@
*
* Copyright (C) 2008 Google, Inc.
* Copyright (C) 2008 HTC Corporation
- * Copyright (c) 2009-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2019, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
*/
#include <linux/types.h>
@@ -327,7 +337,7 @@
rc = audio_aio_open(audio, file);
if (rc < 0) {
- pr_err("%s: audio_aio_open rc=%d\n",
+ pr_err_ratelimited("%s: audio_aio_open rc=%d\n",
__func__, rc);
goto fail;
}
diff --git a/dsp/q6adm.c b/dsp/q6adm.c
index 688012f..edba25f 100644
--- a/dsp/q6adm.c
+++ b/dsp/q6adm.c
@@ -1317,12 +1317,22 @@
switch (opcode) {
case ADM_CMDRSP_GET_PP_PARAMS_V5:
struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v5);
+ if (payload_size < struct_size) {
+ pr_err("%s: payload size %d < expected size %d\n",
+ __func__, payload_size, struct_size);
+ break;
+ }
v5_rsp = (struct adm_cmd_rsp_get_pp_params_v5 *) payload;
data_size = v5_rsp->param_hdr.param_size;
param_data = v5_rsp->param_data;
break;
case ADM_CMDRSP_GET_PP_PARAMS_V6:
struct_size = sizeof(struct adm_cmd_rsp_get_pp_params_v6);
+ if (payload_size < struct_size) {
+ pr_err("%s: payload size %d < expected size %d\n",
+ __func__, payload_size, struct_size);
+ break;
+ }
v6_rsp = (struct adm_cmd_rsp_get_pp_params_v6 *) payload;
data_size = v6_rsp->param_hdr.param_size;
param_data = v6_rsp->param_data;
@@ -1518,6 +1528,11 @@
return 0;
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size < (2 * sizeof(uint32_t))) {
+ pr_err("%s: Invalid payload size %d\n", __func__,
+ data->payload_size);
+ return 0;
+ }
pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n",
__func__, payload[0]);
if (payload[1] != 0) {
@@ -1644,9 +1659,14 @@
case ADM_CMDRSP_DEVICE_OPEN_V5:
case ADM_CMDRSP_DEVICE_OPEN_V6:
case ADM_CMDRSP_DEVICE_OPEN_V8: {
- struct adm_cmd_rsp_device_open_v5 *open =
- (struct adm_cmd_rsp_device_open_v5 *)data->payload;
-
+ struct adm_cmd_rsp_device_open_v5 *open = NULL;
+ if (data->payload_size <
+ sizeof(struct adm_cmd_rsp_device_open_v5)) {
+ pr_err("%s: Invalid payload size %d\n", __func__,
+ data->payload_size);
+ return 0;
+ }
+ open = (struct adm_cmd_rsp_device_open_v5 *)data->payload;
if (open->copp_id == INVALID_COPP_ID) {
pr_err("%s: invalid coppid rxed %d\n",
__func__, open->copp_id);
@@ -2657,7 +2677,7 @@
ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
- ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TS;
} else if (channel_mode == 12) {
ep_payload->dev_channel_mapping[0] = PCM_CHANNEL_FL;
ep_payload->dev_channel_mapping[1] = PCM_CHANNEL_FR;
@@ -2681,7 +2701,7 @@
ep_payload->dev_channel_mapping[6] = PCM_CHANNEL_LB;
ep_payload->dev_channel_mapping[7] = PCM_CHANNEL_RB;
ep_payload->dev_channel_mapping[8] = PCM_CHANNEL_CS;
- ep_payload->dev_channel_mapping[9] = PCM_CHANNELS;
+ ep_payload->dev_channel_mapping[9] = PCM_CHANNEL_TS;
ep_payload->dev_channel_mapping[10] = PCM_CHANNEL_CVH;
ep_payload->dev_channel_mapping[11] = PCM_CHANNEL_MS;
ep_payload->dev_channel_mapping[12] = PCM_CHANNEL_FLC;
@@ -2784,8 +2804,14 @@
if ((topology == VPM_TX_SM_ECNS_V2_COPP_TOPOLOGY) ||
(topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) ||
- (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY))
+ (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)||
+ (topology == VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY)) {
+ if ((rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_8K) &&
+ (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K) &&
+ (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_32K) &&
+ (rate != ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K))
rate = 16000;
+ }
if (topology == VPM_TX_VOICE_SMECNS_V2_COPP_TOPOLOGY)
channel_mode = 1;
@@ -2895,12 +2921,12 @@
open_v8.endpoint_id_2 = 0xFFFF;
open_v8.endpoint_id_3 = 0xFFFF;
- if ((this_adm.ec_ref_rx != -1) &&
- (path != ADM_PATH_PLAYBACK)) {
+ if (((this_adm.ec_ref_rx & AFE_PORT_INVALID) !=
+ AFE_PORT_INVALID) &&
+ (path != ADM_PATH_PLAYBACK)) {
if (this_adm.num_ec_ref_rx_chans != 0) {
open_v8.endpoint_id_2 =
this_adm.ec_ref_rx;
- this_adm.ec_ref_rx = -1;
} else {
pr_err("%s: EC channels not set %d\n",
__func__,
@@ -2944,7 +2970,6 @@
if (this_adm.ec_ref_rx_bit_width != 0) {
ep2_payload.bit_width =
this_adm.ec_ref_rx_bit_width;
- this_adm.ec_ref_rx_bit_width = 0;
} else {
ep2_payload.bit_width = bit_width;
}
@@ -2952,7 +2977,6 @@
if (this_adm.ec_ref_rx_sampling_rate != 0) {
ep2_payload.sample_rate =
this_adm.ec_ref_rx_sampling_rate;
- this_adm.ec_ref_rx_sampling_rate = 0;
} else {
ep2_payload.sample_rate = rate;
}
@@ -3022,7 +3046,6 @@
if (this_adm.ec_ref_rx && (path != 1) &&
(afe_get_port_type(tmp_port) == MSM_AFE_PORT_TYPE_TX)) {
open.endpoint_id_2 = this_adm.ec_ref_rx;
- this_adm.ec_ref_rx = -1;
}
open.topology_id = topology;
@@ -3054,12 +3077,10 @@
open_v6.hdr.pkt_size = sizeof(open_v6);
open_v6.dev_num_channel_eid2 =
this_adm.num_ec_ref_rx_chans;
- this_adm.num_ec_ref_rx_chans = 0;
if (this_adm.ec_ref_rx_bit_width != 0) {
open_v6.bit_width_eid2 =
this_adm.ec_ref_rx_bit_width;
- this_adm.ec_ref_rx_bit_width = 0;
} else {
open_v6.bit_width_eid2 = bit_width;
}
@@ -3067,7 +3088,6 @@
if (this_adm.ec_ref_rx_sampling_rate != 0) {
open_v6.sample_rate_eid2 =
this_adm.ec_ref_rx_sampling_rate;
- this_adm.ec_ref_rx_sampling_rate = 0;
} else {
open_v6.sample_rate_eid2 = rate;
}
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 1b5c94d..7f170ea 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/debugfs.h>
@@ -76,6 +75,11 @@
Q6AFE_MSM_SPKR_FTM_MODE
};
+enum {
+ APTX_AD_48 = 0,
+ APTX_AD_44_1 = 1
+};
+
struct wlock {
struct wakeup_source ws;
};
@@ -460,6 +464,15 @@
schedule_work(&this_afe.afe_spdif_work);
}
+static bool afe_token_is_valid(uint32_t token)
+{
+ if (token >= AFE_MAX_PORTS) {
+ pr_err("%s: token %d is invalid.\n", __func__, token);
+ return false;
+ }
+ return true;
+}
+
static int32_t afe_callback(struct apr_client_data *data, void *priv)
{
if (!data) {
@@ -536,7 +549,10 @@
data->payload_size))
return -EINVAL;
}
- wake_up(&this_afe.wait[data->token]);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
} else if (data->opcode == AFE_EVENT_MBHC_DETECTION_SW_WA) {
msm_aud_evt_notifier_call_chain(SWR_WAKE_IRQ_EVENT, NULL);
} else if (data->payload_size) {
@@ -572,7 +588,10 @@
case AFE_SVC_CMD_SET_PARAM_V2:
case AFE_PORT_CMD_MOD_EVENT_CFG:
atomic_set(&this_afe.state, 0);
- wake_up(&this_afe.wait[data->token]);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
break;
case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER:
break;
@@ -584,7 +603,10 @@
break;
case AFE_CMD_ADD_TOPOLOGIES:
atomic_set(&this_afe.state, 0);
- wake_up(&this_afe.wait[data->token]);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n",
__func__, payload[1]);
break;
@@ -608,7 +630,10 @@
return 0;
}
atomic_set(&this_afe.state, payload[1]);
- wake_up(&this_afe.wait[data->token]);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
break;
case AFE_SVC_CMD_EVENT_CFG:
atomic_set(&this_afe.state, payload[1]);
@@ -632,7 +657,10 @@
else
this_afe.mmap_handle = payload[0];
atomic_set(&this_afe.state, 0);
- wake_up(&this_afe.wait[data->token]);
+ if (afe_token_is_valid(data->token))
+ wake_up(&this_afe.wait[data->token]);
+ else
+ return -EINVAL;
} else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) {
port_id = (uint16_t)(0x0000FFFF & payload[0]);
} else if (data->opcode == AFE_PORT_MOD_EVENT) {
@@ -3541,6 +3569,29 @@
goto exit;
}
break;
+ case ASM_MEDIA_FMT_APTX_ADAPTIVE:
+ if (!cfg->abr_dec_cfg.is_abr_enabled) {
+ pr_debug("%s: sending aptx adaptive congestion buffer size to dsp\n",
+ __func__);
+ param_hdr.param_id =
+ AFE_DECODER_PARAM_ID_CONGESTION_BUFFER_SIZE;
+ param_hdr.param_size =
+ sizeof(struct avs_dec_congestion_buffer_param_t);
+ dec_buffer_id_param.version = 0;
+ dec_buffer_id_param.max_nr_buffers = 226;
+ dec_buffer_id_param.pre_buffer_size = 226;
+ ret = q6afe_pack_and_set_param_in_band(port_id,
+ q6audio_get_port_index(port_id),
+ param_hdr,
+ (u8 *) &dec_buffer_id_param);
+ if (ret) {
+ pr_err("%s: aptx adaptive congestion buffer size for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto exit;
+ }
+ break;
+ }
+ /* fall through for abr enabled case */
default:
pr_debug("%s:sending AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION to DSP payload\n",
__func__);
@@ -3561,7 +3612,7 @@
break;
}
- pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__);
+ pr_debug("%s: Send AFE_API_VERSION_PORT_MEDIA_TYPE to DSP\n", __func__);
param_hdr.module_id = AFE_MODULE_PORT;
param_hdr.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE;
param_hdr.param_size = sizeof(struct afe_port_media_type_t);
@@ -3575,6 +3626,15 @@
media_type.sample_rate =
cfg->data.sbc_config.sample_rate;
break;
+ case ASM_MEDIA_FMT_APTX_ADAPTIVE:
+ if (!cfg->abr_dec_cfg.is_abr_enabled) {
+ media_type.sample_rate =
+ (cfg->data.aptx_ad_config.sample_rate == APTX_AD_44_1) ?
+ AFE_PORT_SAMPLE_RATE_44_1K :
+ AFE_PORT_SAMPLE_RATE_48K;
+ break;
+ }
+ /* fall through for abr enabled case */
default:
media_type.sample_rate =
afe_config.slim_sch.sample_rate;
@@ -3600,11 +3660,19 @@
goto exit;
}
- if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2) {
+ if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2 &&
+ format != ASM_MEDIA_FMT_APTX_ADAPTIVE) {
pr_debug("%s:Unsuppported dec format. Ignore AFE config %u\n",
__func__, format);
goto exit;
}
+
+ if (format == ASM_MEDIA_FMT_APTX_ADAPTIVE &&
+ cfg->abr_dec_cfg.is_abr_enabled) {
+ pr_debug("%s: Ignore AFE config for abr case\n", __func__);
+ goto exit;
+ }
+
pr_debug("%s: sending AFE_DECODER_PARAM_ID_DEC_MEDIA_FMT to DSP payload\n",
__func__);
param_hdr.module_id = AFE_MODULE_ID_DECODER;
@@ -3623,9 +3691,10 @@
switch (cfg->format) {
case ASM_MEDIA_FMT_AAC_V2:
+ case ASM_MEDIA_FMT_APTX_ADAPTIVE:
param_hdr.param_size = sizeof(struct afe_dec_media_fmt_t);
- pr_debug("%s:send AFE_DECODER_PARAM_ID DEC_MEDIA_FMT to DSP payload\n",
+ pr_debug("%s:send AVS_DECODER_PARAM_ID DEC_MEDIA_FMT to DSP payload\n",
__func__);
param_hdr.param_id = AVS_DECODER_PARAM_ID_DEC_MEDIA_FMT;
dec_media_fmt.dec_media_config = cfg->data;
@@ -3634,7 +3703,7 @@
param_hdr,
(u8 *) &dec_media_fmt);
if (ret) {
- pr_err("%s: AFE_DECODER_PARAM_ID DEC_MEDIA_FMT for port 0x%x failed %d\n",
+ pr_err("%s: AVS_DECODER_PARAM_ID DEC_MEDIA_FMT for port 0x%x failed %d\n",
__func__, port_id, ret);
goto exit;
}
@@ -7282,7 +7351,16 @@
return ret;
}
-
+/**
+ * afe_enable_lpass_core_shared_clock -
+ * Configures the core clk on LPASS.
+ * Need on targets where lpass provides
+ * clocks
+ * @port_id: afe port id
+ * @enable: enable or disable clk
+ *
+ * Returns success or failure of call.
+ */
int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable)
{
struct afe_param_id_lpass_core_shared_clk_cfg clk_cfg;
@@ -7321,7 +7399,16 @@
mutex_unlock(&this_afe.afe_cmd_lock);
return ret;
}
+EXPORT_SYMBOL(afe_enable_lpass_core_shared_clock);
+/**
+ * q6afe_check_osr_clk_freq -
+ * Gets supported OSR CLK frequencies
+ *
+ * @freq: frequency to check
+ *
+ * Returns success if freq is supported.
+ */
int q6afe_check_osr_clk_freq(u32 freq)
{
int ret = 0;
@@ -7346,6 +7433,7 @@
}
return ret;
}
+EXPORT_SYMBOL(q6afe_check_osr_clk_freq);
int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
{
diff --git a/dsp/q6asm.c b/dsp/q6asm.c
index acdd759..934fc58 100644
--- a/dsp/q6asm.c
+++ b/dsp/q6asm.c
@@ -1713,14 +1713,14 @@
return 0;
}
- if (data->payload_size > sizeof(int)) {
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
__func__, payload[0], payload[1], data->opcode,
data->token, data->payload_size, data->src_port,
data->dest_port, asm_token._token.session_id, dir);
pr_debug("%s:Payload = [0x%x] status[0x%x]\n",
__func__, payload[0], payload[1]);
- } else if (data->payload_size == sizeof(int)) {
+ } else if (data->payload_size == sizeof(uint32_t)) {
pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
__func__, payload[0], data->opcode,
data->token, data->payload_size, data->src_port,
@@ -1734,7 +1734,7 @@
case ASM_CMD_SHARED_MEM_MAP_REGIONS:
case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
case ASM_CMD_ADD_TOPOLOGIES:
- if (payload[1] != 0) {
+ if (data->payload_size >= 2 * sizeof(uint32_t) && payload[1] != 0) {
pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n",
__func__, payload[0], payload[1],
asm_token._token.session_id);
@@ -1752,8 +1752,12 @@
if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
wake_up(&ac->mem_wait);
- dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
__func__, payload[0], payload[1]);
+ else
+ dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
break;
default:
pr_debug("%s: command[0x%x] not expecting rsp\n",
@@ -1782,8 +1786,13 @@
break;
}
case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{
- pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
- __func__, payload[0], payload[1]);
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ else
+ pr_debug("%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
+
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
wake_up(&ac->mem_wait);
@@ -1792,8 +1801,12 @@
break;
}
default:
- pr_debug("%s: command[0x%x]success [0x%x]\n",
- __func__, payload[0], payload[1]);
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: command[0x%x]success [0x%x]\n",
+ __func__, payload[0], payload[1]);
+ else
+ pr_debug("%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
}
if (ac->cb)
ac->cb(data->opcode, data->token,
@@ -1944,8 +1957,12 @@
&(session[session_id].session_lock), flags);
return -EINVAL;
}
- dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
- __func__, payload[0], payload[1], data->opcode);
+ if(data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
+ __func__, payload[0], payload[1], data->opcode);
+ else
+ dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n",
+ __func__, data->payload_size);
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
switch (payload[0]) {
@@ -1991,32 +2008,37 @@
case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS:
case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
- pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
- __func__, ac->session,
- data->opcode, data->token,
- payload[0], payload[1],
- data->src_port, data->dest_port);
- if (payload[1] != 0) {
- pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
- __func__, payload[0], payload[1]);
- if (wakeup_flag) {
- if ((is_adsp_reg_event(payload[0]) >=
- 0) ||
- (payload[0] ==
- ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
- (payload[0] ==
- ASM_STREAM_CMD_SET_PP_PARAMS_V3))
- atomic_set(&ac->cmd_state_pp,
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ payload[0], payload[1],
+ data->src_port, data->dest_port);
+ if (payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ if (wakeup_flag) {
+ if ((is_adsp_reg_event(payload[0]) >=
+ 0) ||
+ (payload[0] ==
+ ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
+ (payload[0] ==
+ ASM_STREAM_CMD_SET_PP_PARAMS_V3))
+ atomic_set(&ac->cmd_state_pp,
payload[1]);
- else
- atomic_set(&ac->cmd_state,
+ else
+ atomic_set(&ac->cmd_state,
payload[1]);
- wake_up(&ac->cmd_wait);
+ wake_up(&ac->cmd_wait);
+ }
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return 0;
}
- spin_unlock_irqrestore(
- &(session[session_id].session_lock),
- flags);
- return 0;
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
}
if ((is_adsp_reg_event(payload[0]) >= 0) ||
(payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) ||
@@ -2038,19 +2060,24 @@
(uint32_t *)data->payload, ac->priv);
break;
case ASM_CMD_ADD_TOPOLOGIES:
- pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
- __func__, payload[0], payload[1]);
- if (payload[1] != 0) {
- pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
- __func__, payload[0], payload[1]);
- if (wakeup_flag) {
- atomic_set(&ac->mem_state, payload[1]);
- wake_up(&ac->mem_wait);
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+ __func__, payload[0], payload[1]);
+ if (payload[1] != 0) {
+ pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+ __func__, payload[0], payload[1]);
+ if (wakeup_flag) {
+ atomic_set(&ac->mem_state, payload[1]);
+ wake_up(&ac->mem_wait);
+ }
+ spin_unlock_irqrestore(
+ &(session[session_id].session_lock),
+ flags);
+ return 0;
}
- spin_unlock_irqrestore(
- &(session[session_id].session_lock),
- flags);
- return 0;
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
}
if (atomic_read(&ac->mem_state) && wakeup_flag) {
atomic_set(&ac->mem_state, 0);
@@ -2061,8 +2088,12 @@
(uint32_t *)data->payload, ac->priv);
break;
case ASM_DATA_EVENT_WATERMARK: {
- pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
- __func__, payload[0], payload[1]);
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
+ __func__, payload[0], payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
break;
}
case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
@@ -2074,11 +2105,16 @@
/* error or malformed APR packet. Otherwise */
/* response will be returned as */
/* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */
- if (payload[1] != 0) {
- pr_err("%s: ASM get param error = %d, resuming\n",
- __func__, payload[1]);
- rtac_make_asm_callback(ac->session, payload,
- data->payload_size);
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ if (payload[1] != 0) {
+ pr_err("%s: ASM get param error = %d, resuming\n",
+ __func__, payload[1]);
+ rtac_make_asm_callback(ac->session, payload,
+ data->payload_size);
+ }
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
}
break;
case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
@@ -2086,11 +2122,16 @@
__func__, ac->session,
data->opcode, data->token,
data->src_port, data->dest_port);
- if (payload[1] != 0)
- pr_err("%s: ASM get param error = %d, resuming\n",
- __func__, payload[1]);
- atomic_set(&ac->cmd_state_pp, payload[1]);
- wake_up(&ac->cmd_wait);
+ if (data->payload_size >= 2 * sizeof(uint32_t)) {
+ if (payload[1] != 0)
+ pr_err("%s: ASM get param error = %d, resuming\n",
+ __func__, payload[1]);
+ atomic_set(&ac->cmd_state_pp, payload[1]);
+ wake_up(&ac->cmd_wait);
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ }
break;
default:
pr_debug("%s: command[0x%x] not expecting rsp\n",
@@ -2106,10 +2147,13 @@
switch (data->opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2:{
struct audio_port_data *port = &ac->port[IN];
-
- dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
- __func__, payload[0], payload[1],
- data->token);
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
+ __func__, payload[0], payload[1],
+ data->token);
+ else
+ dev_err(ac->dev, "%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
if (ac->io_mode & SYNC_IO_MODE) {
if (port->buf == NULL) {
pr_err("%s: Unexpected Write Done\n",
@@ -2121,10 +2165,11 @@
}
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
buf_index = asm_token._token.buf_index;
- if (lower_32_bits(port->buf[buf_index].phys) !=
- payload[0] ||
- msm_audio_populate_upper_32_bits(
- port->buf[buf_index].phys) != payload[1]) {
+ if ( data->payload_size >= 2 * sizeof(uint32_t) &&
+ (lower_32_bits(port->buf[buf_index].phys) !=
+ payload[0] ||
+ msm_audio_populate_upper_32_bits(
+ port->buf[buf_index].phys) != payload[1])) {
pr_debug("%s: Expected addr %pK\n",
__func__, &port->buf[buf_index].phys);
pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n",
@@ -2159,14 +2204,25 @@
} else if (generic_get_data) {
generic_get_data->valid = 1;
if (generic_get_data->is_inband) {
- pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
- __func__, payload[1], payload[2], payload[3]);
- generic_get_data->size_in_ints = payload[3]>>2;
- for (i = 0; i < payload[3]>>2; i++) {
- generic_get_data->ints[i] =
- payload[4+i];
- pr_debug("%s: ASM callback val %i = %i\n",
- __func__, i, payload[4+i]);
+ if (data->payload_size >= 4 * sizeof(uint32_t))
+ pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
+ __func__, payload[1], payload[2], payload[3]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__,
+ data->payload_size);
+
+ if (data->payload_size >= (4 + (payload[3]>>2)) * sizeof(uint32_t)) {
+ generic_get_data->size_in_ints = payload[3]>>2;
+ for (i = 0; i < payload[3]>>2; i++) {
+ generic_get_data->ints[i] =
+ payload[4+i];
+ pr_debug("%s: ASM callback val %i = %i\n",
+ __func__, i, payload[4+i]);
+ }
+ } else {
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
}
pr_debug("%s: callback size in ints = %i\n",
__func__,
@@ -2253,11 +2309,16 @@
data->src_port, data->dest_port);
break;
case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
- dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
- __func__,
- payload[0], payload[1], payload[2]);
- ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) |
- payload[1]);
+ if (data->payload_size >= 3 * sizeof(uint32_t)) {
+ dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2]);
+ ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) |
+ payload[1]);
+ } else {
+ dev_err(ac->dev, "%s: payload size of %x is less than expected.n",
+ __func__, data->payload_size);
+ }
if (atomic_cmpxchg(&ac->time_flag, 1, 0))
wake_up(&ac->time_wait);
break;
@@ -2267,19 +2328,30 @@
__func__, ac->session,
data->opcode, data->token,
data->src_port, data->dest_port);
- pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
- __func__,
- payload[0], payload[1], payload[2],
- payload[3]);
+ if (data->payload_size >= 4 * sizeof(uint32_t))
+ pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
+ __func__,
+ payload[0], payload[1], payload[2],
+ payload[3]);
+ else
+ pr_debug("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
break;
case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2:
q6asm_process_mtmx_get_param_rsp(ac, (void *) payload);
break;
case ASM_STREAM_PP_EVENT:
case ASM_STREAM_CMD_ENCDEC_EVENTS:
- case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE:
- pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]",
- __func__, payload[0], payload[1]);
+ case ASM_IEC_61937_MEDIA_FMT_EVENT:
+ if (data->payload_size >= 2 * sizeof(uint32_t))
+ pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]",
+ __func__, payload[0], payload[1]);
+ else if (data->payload_size >= sizeof(uint32_t))
+ pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x]",
+ __func__, payload[0]);
+ else
+ pr_debug("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
i = is_adsp_raise_event(data->opcode);
if (i < 0) {
spin_unlock_irqrestore(
@@ -2311,6 +2383,34 @@
pp_event_package->payload_len = payload_size;
memcpy((void *)pp_event_package->payload,
data->payload, payload_size);
+ if ((data->opcode == ASM_IEC_61937_MEDIA_FMT_EVENT) &&
+ (payload_size == 4)) {
+ switch (payload[0]) {
+ case ASM_MEDIA_FMT_AC3:
+ ((uint32_t *)pp_event_package->payload)[0] =
+ SND_AUDIOCODEC_AC3;
+ break;
+ case ASM_MEDIA_FMT_EAC3:
+ ((uint32_t *)pp_event_package->payload)[0] =
+ SND_AUDIOCODEC_EAC3;
+ break;
+ case ASM_MEDIA_FMT_DTS:
+ ((uint32_t *)pp_event_package->payload)[0] =
+ SND_AUDIOCODEC_DTS;
+ break;
+ case ASM_MEDIA_FMT_TRUEHD:
+ ((uint32_t *)pp_event_package->payload)[0] =
+ SND_AUDIOCODEC_TRUEHD;
+ break;
+ case ASM_MEDIA_FMT_AAC_V2:
+ ((uint32_t *)pp_event_package->payload)[0] =
+ SND_AUDIOCODEC_AAC;
+ break;
+ default:
+ pr_debug("%s: Event with unknown media_fmt 0x%x\n",
+ __func__, payload[0]);
+ }
+ }
ac->cb(data->opcode, data->token,
(void *)pp_event_package, ac->priv);
kfree(pp_event_package);
@@ -2318,16 +2418,24 @@
&(session[session_id].session_lock), flags);
return 0;
case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2:
- pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
- __func__, ac->session, payload[0], payload[2],
- payload[1]);
+ if (data->payload_size >= 3 * sizeof(uint32_t))
+ pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n",
+ __func__, ac->session, payload[0], payload[2],
+ payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
wake_up(&ac->cmd_wait);
break;
case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
- pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
- __func__, ac->session, payload[0], payload[2],
- payload[1]);
- if (payload[0] == 0) {
+ if (data->payload_size >= 3 * sizeof(uint32_t))
+ pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
+ __func__, ac->session, payload[0], payload[2],
+ payload[1]);
+ else
+ pr_err("%s: payload size of %x is less than expected.\n",
+ __func__, data->payload_size);
+ if (payload[0] == 0 && data->payload_size >= 2 * sizeof(uint32_t)) {
atomic_set(&ac->cmd_state, 0);
/* ignore msw, as a delay that large shouldn't happen */
ac->path_delay = payload[1];
diff --git a/dsp/q6usm.c b/dsp/q6usm.c
index ce25c71..70c3a6b 100644
--- a/dsp/q6usm.c
+++ b/dsp/q6usm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/mutex.h>
#include <linux/wait.h>
@@ -490,6 +490,11 @@
uint32_t token;
uint32_t *payload = data->payload;
+ if (data->payload_size < (2 * sizeof(uint32_t))) {
+ pr_err("%s: payload has invalid size[%d]\n", __func__,
+ data->payload_size);
+ return -EINVAL;
+ }
pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
__func__, payload[0], payload[1], data->opcode);
pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
@@ -551,6 +556,11 @@
}
if (data->opcode == APR_BASIC_RSP_RESULT) {
+ if (data->payload_size < (2 * sizeof(uint32_t))) {
+ pr_err("%s: payload has invalid size[%d]\n", __func__,
+ data->payload_size);
+ return -EINVAL;
+ }
/* status field check */
if (payload[1]) {
pr_err("%s: wrong response[%d] on cmd [%d]\n",
@@ -614,6 +624,12 @@
opcode = Q6USM_EVENT_READ_DONE;
spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+ if (data->payload_size <
+ (sizeof(uint32_t)*(READDONE_IDX_STATUS + 1))) {
+ pr_err("%s: Invalid payload size for READDONE[%d]\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
if (payload[READDONE_IDX_STATUS]) {
pr_err("%s: wrong READDONE[%d]; token[%d]\n",
__func__,
@@ -660,6 +676,12 @@
struct us_port_data *port = &usc->port[IN];
opcode = Q6USM_EVENT_WRITE_DONE;
+ if (data->payload_size <
+ (sizeof(uint32_t)*(WRITEDONE_IDX_STATUS + 1))) {
+ pr_err("%s: Invalid payload size for WRITEDONE[%d]\n",
+ __func__, data->payload_size);
+ return -EINVAL;
+ }
if (payload[WRITEDONE_IDX_STATUS]) {
pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
__func__,
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index 9daebe5..bceab12 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -254,6 +254,9 @@
/* Sample rate is 16000 Hz.*/
#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K 16000
+/* Sample rate is 32000 Hz.*/
+#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_32K 32000
+
/* Sample rate is 48000 Hz.*/
#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K 48000
@@ -643,8 +646,9 @@
#define ASM_STREAM_CMD_REGISTER_PP_EVENTS 0x00013213
#define ASM_STREAM_PP_EVENT 0x00013214
-#define ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE 0x13333
-#define ASM_IEC_61937_MEDIA_FMT_EVENT 0x13334
+#define ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE 0x1321C
+#define ASM_IEC_61937_MEDIA_FMT_EVENT 0x1321D
+
#define DSP_STREAM_CMD "ADSP Stream Cmd"
#define DSP_STREAM_CALLBACK "ADSP Stream Callback Event"
@@ -2477,6 +2481,7 @@
#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C
#define AFE_PORT_SAMPLE_RATE_8K 8000
#define AFE_PORT_SAMPLE_RATE_16K 16000
+#define AFE_PORT_SAMPLE_RATE_44_1K 44100
#define AFE_PORT_SAMPLE_RATE_48K 48000
#define AFE_PORT_SAMPLE_RATE_96K 96000
#define AFE_PORT_SAMPLE_RATE_176P4K 176400
@@ -3795,6 +3800,7 @@
struct afe_abr_dec_cfg_t {
struct afe_imc_dec_enc_info imc_info;
+ bool is_abr_enabled;
} __packed;
struct afe_abr_enc_cfg_t {
@@ -4437,6 +4443,19 @@
*/
} __packed;
+/*
+ * Payload of the APTX AD decoder configuration parameters in the
+ * #ASM_MEDIA_FMT_APTX_ADAPTIVE media format.
+ */
+struct asm_aptx_ad_dec_cfg_t {
+ uint32_t sample_rate;
+ /*
+ * Number of samples per second.
+ *
+ * @values 0x0(48000Hz), 0x1(44100Hz)
+ */
+} __packed;
+
union afe_enc_config_data {
struct asm_sbc_enc_cfg_t sbc_config;
struct asm_aac_enc_cfg_t aac_config;
@@ -4458,6 +4477,7 @@
struct asm_sbc_dec_cfg_t sbc_config;
struct asm_aac_dec_cfg_v2_t aac_config;
struct asm_mp3_dec_cfg_t mp3_config;
+ struct asm_aptx_ad_dec_cfg_t aptx_ad_config;
};
struct afe_dec_config {
@@ -5082,6 +5102,7 @@
#define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86
#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX 0x10015002
#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE 0x10028000
+#define VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY 0x10000005
/* Memory map regions command payload used by the
* #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS
@@ -5396,7 +5417,7 @@
#define PCM_CHANNEL_RB 9
/* Top surround channel. */
-#define PCM_CHANNELS 10
+#define PCM_CHANNEL_TS 10
/* Center vertical height channel.*/
#define PCM_CHANNEL_CVH 11
@@ -5476,6 +5497,9 @@
/* Right side direct channel. */
#define PCM_CHANNEL_RSD 34
+/* Mark unused channel. */
+#define PCM_CHANNEL_UNUSED 47
+
#define PCM_FORMAT_MAX_NUM_CHANNEL 8
#define PCM_FORMAT_MAX_CHANNELS_9 9
@@ -5605,7 +5629,7 @@
* - #PCM_CHANNEL_CS
* - #PCM_CHANNEL_LB
* - #PCM_CHANNEL_RB
- * - #PCM_CHANNELS
+ * - #PCM_CHANNEL_TS
* - #PCM_CHANNEL_CVH
* - #PCM_CHANNEL_MS
* - #PCM_CHANNEL_FLC
@@ -9539,7 +9563,7 @@
* - #PCM_CHANNEL_CS
* - #PCM_CHANNEL_LB
* - #PCM_CHANNEL_RB
- * - #PCM_CHANNELS
+ * - #PCM_CHANNEL_TS
* - #PCM_CHANNEL_CVH
* - #PCM_CHANNEL_MS
* - #PCM_CHANNEL_FLC
@@ -9611,7 +9635,7 @@
* - #PCM_CHANNEL_CS
* - #PCM_CHANNEL_LB
* - #PCM_CHANNEL_RB
- * - #PCM_CHANNELS
+ * - #PCM_CHANNEL_TS
* - #PCM_CHANNEL_CVH
* - #PCM_CHANNEL_MS
* - #PCM_CHANNEL_FLC
diff --git a/ipc/apr_tal_rpmsg.c b/ipc/apr_tal_rpmsg.c
index 723de56..c8830d1 100644
--- a/ipc/apr_tal_rpmsg.c
+++ b/ipc/apr_tal_rpmsg.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/rpmsg.h>
#include <ipc/apr_tal.h>
+#include <linux/of_device.h>
enum apr_channel_state {
APR_CH_DISCONNECTED,
@@ -125,14 +126,13 @@
int rc = 0;
struct apr_svc_ch_dev *apr_ch = NULL;
- if ((clnt != APR_CLIENT_AUDIO) || (dest != APR_DEST_QDSP6) ||
+ if ((clnt != APR_CLIENT_AUDIO) || (dest >= APR_DEST_MAX) ||
(dl != APR_DL_SMD)) {
pr_err("%s: Invalid params, clnt:%d, dest:%d, dl:%d\n",
__func__, clnt, dest, dl);
return NULL;
}
-
- apr_ch = &apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
+ apr_ch = &apr_svc_ch[dl][dest][clnt];
mutex_lock(&apr_ch->m_lock);
if (!apr_ch->handle) {
rc = wait_event_timeout(apr_ch->wait,
@@ -207,24 +207,46 @@
static int apr_tal_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct apr_svc_ch_dev *apr_ch = NULL;
+ int ret = 0;
+ const char* dest;
+
+ ret = of_property_read_string(rpdev->dev.parent->of_node,
+ "mbox-names", &dest);
+
+ if(ret < 0){
+ pr_err("%s no parent source pid found\n", __func__);
+ return -EINVAL;
+ }
if (!strcmp(rpdev->id.name, "apr_audio_svc")) {
dev_info(&rpdev->dev, "%s: Channel[%s] state[Up]\n",
__func__, rpdev->id.name);
-
+ } else {
+ dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
+ __func__, rpdev->id.name);
+ return -EINVAL;
+ }
+ if(strstr(dest, "adsp")) {
apr_ch =
&apr_svc_ch[APR_DL_SMD][APR_DEST_QDSP6][APR_CLIENT_AUDIO];
apr_ch->handle = rpdev;
apr_ch->channel_state = APR_CH_CONNECTED;
dev_set_drvdata(&rpdev->dev, apr_ch);
wake_up(&apr_ch->wait);
+ } else if(strstr(dest, "mpss")) {
+ apr_ch =
+ &apr_svc_ch[APR_DL_SMD][APR_DEST_MODEM][APR_CLIENT_AUDIO];
+ apr_ch->handle = rpdev;
+ apr_ch->channel_state = APR_CH_CONNECTED;
+ dev_set_drvdata(&rpdev->dev, apr_ch);
+ wake_up(&apr_ch->wait);
} else {
- dev_err(&rpdev->dev, "%s, Invalid Channel [%s]\n",
- __func__, rpdev->id.name);
+ dev_err(&rpdev->dev, "%s, unsupported dest %s\n",
+ __func__, dest);
return -EINVAL;
}
- return 0;
+ return ret;
}
static void apr_tal_rpmsg_remove(struct rpmsg_device *rpdev)
diff --git a/ipc/apr_v3.c b/ipc/apr_v3.c
index 7bf4774..4160bee 100644
--- a/ipc/apr_v3.c
+++ b/ipc/apr_v3.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, 2019 The Linux Foundation. All rights reserved.
*/
#include <linux/types.h>
@@ -13,10 +13,16 @@
#define DEST_ID APR_DEST_MODEM
+/**
+ * apr_get_subsys_state - get modem subsys status
+ *
+ * Returns apr_subsys_state
+ */
enum apr_subsys_state apr_get_subsys_state(void)
{
return apr_get_modem_state();
}
+EXPORT_SYMBOL(apr_get_subsys_state);
void apr_set_subsys_state(void)
{
diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c
index 4212816..bef9cba 100644
--- a/soc/pinctrl-lpi.c
+++ b/soc/pinctrl-lpi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/gpio.h>
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/clk.h>
+#include <linux/bitops.h>
#include <soc/snd_event.h>
#include <linux/pm_runtime.h>
#include <dsp/audio_notifier.h>
@@ -21,39 +22,45 @@
#include "core.h"
#include "pinctrl-utils.h"
-#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
+#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
-#define LPI_ADDRESS_SIZE 0x20000
+#define LPI_ADDRESS_SIZE 0x20000
+#define LPI_SLEW_ADDRESS_SIZE 0x1000
-#define LPI_GPIO_REG_VAL_CTL 0x00
-#define LPI_GPIO_REG_DIR_CTL 0x04
+#define LPI_GPIO_REG_VAL_CTL 0x00
+#define LPI_GPIO_REG_DIR_CTL 0x04
-#define LPI_GPIO_REG_PULL_SHIFT 0x0
-#define LPI_GPIO_REG_PULL_MASK 0x3
+#define LPI_SLEW_REG_VAL_CTL 0x00
+#define LPI_SLEW_RATE_MAX 0x03
+#define LPI_SLEW_BITS_SIZE 0x02
+#define LPI_SLEW_OFFSET_INVALID 0xFFFFFFFF
-#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
-#define LPI_GPIO_REG_FUNCTION_MASK 0x3C
+#define LPI_GPIO_REG_PULL_SHIFT 0x0
+#define LPI_GPIO_REG_PULL_MASK 0x3
-#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
-#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0
+#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2
+#define LPI_GPIO_REG_FUNCTION_MASK 0x3C
-#define LPI_GPIO_REG_OE_SHIFT 0x9
-#define LPI_GPIO_REG_OE_MASK 0x200
+#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6
+#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0
-#define LPI_GPIO_REG_DIR_SHIFT 0x1
-#define LPI_GPIO_REG_DIR_MASK 0x2
+#define LPI_GPIO_REG_OE_SHIFT 0x9
+#define LPI_GPIO_REG_OE_MASK 0x200
-#define LPI_GPIO_BIAS_DISABLE 0x0
-#define LPI_GPIO_PULL_DOWN 0x1
-#define LPI_GPIO_KEEPER 0x2
-#define LPI_GPIO_PULL_UP 0x3
+#define LPI_GPIO_REG_DIR_SHIFT 0x1
+#define LPI_GPIO_REG_DIR_MASK 0x2
-#define LPI_GPIO_FUNC_GPIO "gpio"
-#define LPI_GPIO_FUNC_FUNC1 "func1"
-#define LPI_GPIO_FUNC_FUNC2 "func2"
-#define LPI_GPIO_FUNC_FUNC3 "func3"
-#define LPI_GPIO_FUNC_FUNC4 "func4"
-#define LPI_GPIO_FUNC_FUNC5 "func5"
+#define LPI_GPIO_BIAS_DISABLE 0x0
+#define LPI_GPIO_PULL_DOWN 0x1
+#define LPI_GPIO_KEEPER 0x2
+#define LPI_GPIO_PULL_UP 0x3
+
+#define LPI_GPIO_FUNC_GPIO "gpio"
+#define LPI_GPIO_FUNC_FUNC1 "func1"
+#define LPI_GPIO_FUNC_FUNC2 "func2"
+#define LPI_GPIO_FUNC_FUNC3 "func3"
+#define LPI_GPIO_FUNC_FUNC4 "func4"
+#define LPI_GPIO_FUNC_FUNC5 "func5"
static bool lpi_dev_up;
static struct device *lpi_dev;
@@ -70,30 +77,39 @@
/**
* struct lpi_gpio_pad - keep current GPIO settings
- * @offset: Nth GPIO in supported GPIOs.
+ * @offset: stores one of gpio_offset or slew_offset at a given time.
+ * @gpio_offset: Nth GPIO in supported GPIOs.
+ * @slew_offset: Nth GPIO's position in slew register in supported GPIOs.
* @output_enabled: Set to true if GPIO output logic is enabled.
* @value: value of a pin
- * @base: Address base of LPI GPIO PAD.
+ * @base: stores one of gpio_base or slew_base at a given time.
+ * @gpio_base: Address base of LPI GPIO PAD.
+ * @slew_base: Address base of LPI SLEW PAD.
* @pullup: Constant current which flow through GPIO output buffer.
* @strength: No, Low, Medium, High
* @function: See lpi_gpio_functions[]
*/
struct lpi_gpio_pad {
- u32 offset;
- bool output_enabled;
- bool value;
- char __iomem *base;
- unsigned int pullup;
- unsigned int strength;
- unsigned int function;
+ u32 offset;
+ u32 gpio_offset;
+ u32 slew_offset;
+ bool output_enabled;
+ bool value;
+ char __iomem *base;
+ char __iomem *gpio_base;
+ char __iomem *slew_base;
+ unsigned int pullup;
+ unsigned int strength;
+ unsigned int function;
};
struct lpi_gpio_state {
- struct device *dev;
- struct pinctrl_dev *ctrl;
- struct gpio_chip chip;
- char __iomem *base;
- struct clk *lpass_npa_rsc_island;
+ struct device *dev;
+ struct pinctrl_dev *ctrl;
+ struct gpio_chip chip;
+ char __iomem *base;
+ struct clk *lpass_npa_rsc_island;
+ struct mutex slew_access_lock;
};
static const char *const lpi_gpio_groups[] = {
@@ -106,6 +122,7 @@
#define LPI_TLMM_MAX_PINS 100
static u32 lpi_offset[LPI_TLMM_MAX_PINS];
+static u32 lpi_slew_offset[LPI_TLMM_MAX_PINS];
static const char *const lpi_gpio_functions[] = {
[LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO,
@@ -272,7 +289,9 @@
{
struct lpi_gpio_pad *pad;
unsigned int param, arg;
- int i, ret = 0, val;
+ int i, ret = 0;
+ volatile unsigned long val;
+ struct lpi_gpio_state *state = dev_get_drvdata(pctldev->dev);
pad = pctldev->desc->pins[pin].drv_data;
@@ -306,12 +325,44 @@
case PIN_CONFIG_DRIVE_STRENGTH:
pad->strength = arg;
break;
+ case PIN_CONFIG_SLEW_RATE:
+ if (pad->slew_base == NULL ||
+ pad->slew_offset == LPI_SLEW_OFFSET_INVALID) {
+ dev_dbg(pctldev->dev, "%s: invalid slew settings for pin: %d\n",
+ __func__, pin);
+ goto set_gpio;
+ }
+ if (arg > LPI_SLEW_RATE_MAX) {
+ dev_err(pctldev->dev, "%s: invalid slew rate %u for pin: %d\n",
+ __func__, arg, pin);
+ goto set_gpio;
+ }
+ pad->base = pad->slew_base;
+ pad->offset = 0;
+ mutex_lock(&state->slew_access_lock);
+ val = lpi_gpio_read(pad, LPI_SLEW_REG_VAL_CTL);
+ pad->offset = pad->slew_offset;
+ for (i = 0; i < LPI_SLEW_BITS_SIZE; i++) {
+ if (arg & 0x01)
+ set_bit(pad->offset, &val);
+ else
+ clear_bit(pad->offset, &val);
+ pad->offset++;
+ arg = arg >> 1;
+ }
+ pad->offset = 0;
+ lpi_gpio_write(pad, LPI_SLEW_REG_VAL_CTL, val);
+ mutex_unlock(&state->slew_access_lock);
+ break;
default:
ret = -EINVAL;
goto done;
}
}
+set_gpio:
+ pad->base = pad->gpio_base;
+ pad->offset = pad->gpio_offset;
val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL);
val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK |
LPI_GPIO_REG_OE_MASK);
@@ -502,7 +553,8 @@
struct lpi_gpio_state *state;
int ret, npins, i;
char __iomem *lpi_base;
- u32 reg;
+ char __iomem *slew_base;
+ u32 reg, slew_reg;
struct clk *lpass_npa_rsc_island = NULL;
ret = of_property_read_u32(dev->of_node, "reg", ®);
@@ -524,6 +576,16 @@
return ret;
}
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,lpi-slew-offset-tbl",
+ lpi_slew_offset, npins);
+ if (ret < 0) {
+ for (i = 0; i < npins; i++)
+ lpi_slew_offset[i] = LPI_SLEW_OFFSET_INVALID;
+ dev_dbg(dev, "%s: error in reading lpi slew offset table: %d\n",
+ __func__, ret);
+ }
+
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
@@ -532,6 +594,23 @@
state->dev = &pdev->dev;
+ slew_reg = 0;
+ ret = of_property_read_u32(dev->of_node, "qcom,slew-reg", &slew_reg);
+ if (!ret) {
+ slew_base = devm_ioremap(dev, slew_reg, LPI_SLEW_ADDRESS_SIZE);
+ if (slew_base == NULL) {
+ dev_err(dev,
+ "%s devm_ioremap failed for slew rate reg\n",
+ __func__);
+ ret = -ENOMEM;
+ goto err_io;
+ }
+ } else {
+ slew_base = NULL;
+ dev_dbg(dev, "error in reading lpi slew register: %d\n",
+ __func__, ret);
+ }
+
pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL);
if (!pindesc)
return -ENOMEM;
@@ -566,8 +645,13 @@
pindesc->number = i;
pindesc->name = lpi_gpio_groups[i];
- pad->base = lpi_base;
- pad->offset = lpi_offset[i];
+ pad->gpio_base = lpi_base;
+ pad->slew_base = slew_base;
+ pad->base = pad->gpio_base;
+
+ pad->gpio_offset = lpi_offset[i];
+ pad->slew_offset = lpi_slew_offset[i];
+ pad->offset = pad->gpio_offset;
}
state->chip = lpi_gpio_template;
@@ -578,6 +662,8 @@
state->chip.of_gpio_n_cells = 2;
state->chip.can_sleep = false;
+ mutex_init(&state->slew_access_lock);
+
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
if (IS_ERR(state->ctrl))
return PTR_ERR(state->ctrl);
@@ -636,18 +722,23 @@
err_range:
gpiochip_remove(&state->chip);
err_chip:
+ mutex_destroy(&state->slew_access_lock);
+err_io:
return ret;
}
static int lpi_pinctrl_remove(struct platform_device *pdev)
{
struct lpi_gpio_state *state = platform_get_drvdata(pdev);
+
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
snd_event_client_deregister(&pdev->dev);
audio_notifier_deregister("lpi_tlmm");
gpiochip_remove(&state->chip);
+ mutex_destroy(&state->slew_access_lock);
+
return 0;
}
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index 2235934..f9dbc24 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -1415,6 +1415,195 @@
return ret;
}
+static irqreturn_t swr_mstr_interrupt_v2(int irq, void *dev)
+{
+ struct swr_mstr_ctrl *swrm = dev;
+ u32 value, intr_sts, intr_sts_masked;
+ u32 temp = 0;
+ u32 status, chg_sts, i;
+ u8 devnum = 0;
+ int ret = IRQ_HANDLED;
+ struct swr_device *swr_dev;
+ struct swr_master *mstr = &swrm->master;
+
+ if (unlikely(swrm_lock_sleep(swrm) == false)) {
+ dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__);
+ return IRQ_NONE;
+ }
+
+ mutex_lock(&swrm->reslock);
+ swrm_clk_request(swrm, true);
+ mutex_unlock(&swrm->reslock);
+
+ intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS);
+ intr_sts_masked = intr_sts & swrm->intr_mask;
+handle_irq:
+ for (i = 0; i < SWRM_INTERRUPT_MAX; i++) {
+ value = intr_sts_masked & (1 << i);
+ if (!value)
+ continue;
+
+ switch (value) {
+ case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ:
+ dev_dbg(swrm->dev, "%s: Trigger irq to slave device\n",
+ __func__);
+ status = swr_master_read(swrm, SWRM_MCP_SLV_STATUS);
+ ret = swrm_find_alert_slave(swrm, status, &devnum);
+ if (ret) {
+ dev_err_ratelimited(swrm->dev,
+ "%s: no slave alert found.spurious interrupt\n",
+ __func__);
+ break;
+ }
+ swrm_cmd_fifo_rd_cmd(swrm, &temp, devnum, 0x0,
+ SWRS_SCP_INT_STATUS_CLEAR_1, 1);
+ swrm_cmd_fifo_wr_cmd(swrm, 0x4, devnum, 0x0,
+ SWRS_SCP_INT_STATUS_CLEAR_1);
+ swrm_cmd_fifo_wr_cmd(swrm, 0x0, devnum, 0x0,
+ SWRS_SCP_INT_STATUS_CLEAR_1);
+
+
+ list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
+ if (swr_dev->dev_num != devnum)
+ continue;
+ if (swr_dev->slave_irq) {
+ do {
+ handle_nested_irq(
+ irq_find_mapping(
+ swr_dev->slave_irq, 0));
+ } while (swr_dev->slave_irq_pending);
+ }
+
+ }
+ break;
+ case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED:
+ dev_dbg(swrm->dev, "%s: SWR new slave attached\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS:
+ status = swr_master_read(swrm, SWRM_MCP_SLV_STATUS);
+ if (status == swrm->slave_status) {
+ dev_dbg(swrm->dev,
+ "%s: No change in slave status: %d\n",
+ __func__, status);
+ break;
+ }
+ chg_sts = swrm_check_slave_change_status(swrm, status,
+ &devnum);
+ switch (chg_sts) {
+ case SWR_NOT_PRESENT:
+ dev_dbg(swrm->dev,
+ "%s: device %d got detached\n",
+ __func__, devnum);
+ break;
+ case SWR_ATTACHED_OK:
+ dev_dbg(swrm->dev,
+ "%s: device %d got attached\n",
+ __func__, devnum);
+ /* enable host irq from slave device*/
+ swrm_cmd_fifo_wr_cmd(swrm, 0xFF, devnum, 0x0,
+ SWRS_SCP_INT_STATUS_CLEAR_1);
+ swrm_cmd_fifo_wr_cmd(swrm, 0x4, devnum, 0x0,
+ SWRS_SCP_INT_STATUS_MASK_1);
+
+ break;
+ case SWR_ALERT:
+ dev_dbg(swrm->dev,
+ "%s: device %d has pending interrupt\n",
+ __func__, devnum);
+ break;
+ }
+ break;
+ case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET:
+ dev_err_ratelimited(swrm->dev,
+ "%s: SWR bus clsh detected\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW:
+ dev_dbg(swrm->dev, "%s: SWR read FIFO overflow\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW:
+ dev_dbg(swrm->dev, "%s: SWR read FIFO underflow\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW:
+ dev_dbg(swrm->dev, "%s: SWR write FIFO overflow\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_CMD_ERROR:
+ value = swr_master_read(swrm, SWRM_CMD_FIFO_STATUS);
+ dev_err_ratelimited(swrm->dev,
+ "%s: SWR CMD error, fifo status 0x%x, flushing fifo\n",
+ __func__, value);
+ swr_master_write(swrm, SWRM_CMD_FIFO_CMD, 0x1);
+ break;
+ case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION:
+ dev_err_ratelimited(swrm->dev,
+ "%s: SWR Port collision detected\n",
+ __func__);
+ swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION;
+ swr_master_write(swrm,
+ SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask);
+ break;
+ case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH:
+ dev_dbg(swrm->dev,
+ "%s: SWR read enable valid mismatch\n",
+ __func__);
+ swrm->intr_mask &=
+ ~SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH;
+ swr_master_write(swrm,
+ SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask);
+ break;
+ case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED:
+ complete(&swrm->broadcast);
+ dev_dbg(swrm->dev, "%s: SWR cmd id finished\n",
+ __func__);
+ break;
+ case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED_V2:
+ break;
+ case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL_V2:
+ break;
+ case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2:
+ break;
+ case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2:
+ break;
+ case SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP:
+ if (swrm->state == SWR_MSTR_UP)
+ dev_dbg(swrm->dev,
+ "%s:SWR Master is already up\n",
+ __func__);
+ else
+ dev_err_ratelimited(swrm->dev,
+ "%s: SWR wokeup during clock stop\n",
+ __func__);
+ break;
+ default:
+ dev_err_ratelimited(swrm->dev,
+ "%s: SWR unknown interrupt value: %d\n",
+ __func__, value);
+ ret = IRQ_NONE;
+ break;
+ }
+ }
+ swr_master_write(swrm, SWRM_INTERRUPT_CLEAR, intr_sts);
+ swr_master_write(swrm, SWRM_INTERRUPT_CLEAR, 0x0);
+
+ intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS);
+ intr_sts_masked = intr_sts & swrm->intr_mask;
+
+ if (intr_sts_masked) {
+ dev_dbg(swrm->dev, "%s: new interrupt received\n", __func__);
+ goto handle_irq;
+ }
+
+ mutex_lock(&swrm->reslock);
+ swrm_clk_request(swrm, false);
+ mutex_unlock(&swrm->reslock);
+ swrm_unlock_sleep(swrm);
+ return ret;
+}
+
static irqreturn_t swrm_wakeup_interrupt(int irq, void *dev)
{
struct swr_mstr_ctrl *swrm = dev;
@@ -1899,7 +2088,7 @@
}
ret = request_threaded_irq(swrm->irq, NULL,
- swr_mstr_interrupt,
+ swr_mstr_interrupt_v2,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"swr_master_irq", swrm);
if (ret) {
diff --git a/soc/swrm_registers.h b/soc/swrm_registers.h
index c53d37b..6f2544b 100644
--- a/soc/swrm_registers.h
+++ b/soc/swrm_registers.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2018-2019 The Linux Foundation. All rights reserved.
*/
#ifndef _SWRM_REGISTERS_H
@@ -49,6 +49,13 @@
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000
+#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED_V2 0x800
+#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL_V2 0x1000
+#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED_V2 0x2000
+#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED_V2 0x4000
+#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST_V2 0x8000
+#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP 0x10000
+
#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204)
#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF