Merge tag 'LA.UM.9.14.r1-24700-LAHAINA.QSSI15.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel into android13-5.4-lahaina

"LA.UM.9.14.r1-24700-LAHAINA.QSSI15.0"

* tag 'LA.UM.9.14.r1-24700-LAHAINA.QSSI15.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel:
  Asoc: dsp: Fix to check the list is empty or not
  dsp: q6lsm: Check size of payload before access
  Fix for OOB access issue
  dsp: q6asm increase the locking range
  ASoC: msm-pcm-q6-v2: Add size check
  ASoC : Add proper copyright marking.
  dsp: afe: Add check for num_spks
  soc: pinctrl-lpi: remove pm ops
  asoc: msm-compress: Fix compress_pause failure on gki
  Audio legacy: Integer overflow in msm_lsm_ioctl_compat during audio playback usecase. size = sizeof(p_info_32) + p_info_32.param_size; This overflow issue may result heap overflow during copying the data: memcpy(param_info_rsp, &p_info_32, sizeof(p_info_32));
  ASoC : Add macro to differentiate auto code
  ASoC: msm: get CoPP index based on FE id
  asoc: codecs: add array bound check
  soc: Address SWR rate mismatch interrupt
  asoc : add configuration about SLIMBUS_7_TX
  asoc: lahaina: add support for aud_ref_clk_sel mux
  dsp: afe: add support for aud_ref_clk_sel mux
  ASoC: dsp: Release lock before return
  ASoC: wcd937x: Add flag to decide RX_MUTE for HPHL and EAR
  ASoC: bolero: Add check for CMPDR switch
  ASoC : add support of HDMI controller for rb3gen2 platform
  asoc: lahaina: fractional sample rate support for TDM
  asoc: support for fractional sample rate over tdm
  Revert "asoc: msm-compress : Fix for CTS-on-gsi with gki"
  audio-kernel:swr: Add dynamic SWRM clk support
  asoc: codec: make mclk freq configurable in Bolero
  asoc: lahaina: Add ext clk source support
  asoc: ext-clk: Add support for configuring ext clk
  dsp: afe: Enhance and expose API for configuring ext clk
  dsp: q6voice: Handle mutex lock-unlock
  asoc: msm-compress : Fix for CTS-on-gsi with gki

Change-Id: I21b8e5f5ef608bf298230419123e4ee37c9b0797
This commit is contained in:
Michael Bestas 2024-05-29 03:04:05 +03:00
commit bb0f246e35
No known key found for this signature in database
GPG key ID: CC95044519BE6669
32 changed files with 1549 additions and 384 deletions

2
techpack/audio/asoc/Kbuild Executable file → Normal file
View file

@ -139,6 +139,8 @@ COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR)
############ ASoC Drivers ############
MACHINE_OBJS += msm-common.o
# for SM8150 sound card driver
ifdef CONFIG_SND_SOC_SM8150
MACHINE_OBJS += sm8150.o

View file

@ -57,6 +57,8 @@ struct audio_ext_clk_priv {
const char *clk_name;
uint32_t lpass_core_hwvote_client_handle;
uint32_t lpass_audio_hwvote_client_handle;
bool supports_ext_mclk;
uint32_t ext_mclk_freq;
};
static struct audio_ext_clk audio_clk_array[];
@ -70,46 +72,75 @@ static int audio_ext_clk_prepare(struct clk_hw *hw)
{
struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
int ret;
int ret = 0;
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) &&
(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
clk_priv->clk_cfg.enable = 1;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
if (ret < 0) {
if (__ratelimit(&rtl))
pr_err_ratelimited("%s afe_set_digital_codec_core_clock failed\n",
__func__);
return ret;
/* Case 1. Clock driver supports ext clk source && platform enables ext clk support
* - Use ext clk API
* Case 2. Clock driver supports ext clk source && platform has NOT enabled ext clk support
* - We try using ext clk API, it fails; we fall back to internal clk API
* Case 3. Clock driver doesn't support ext clk source
* - Use internal clk API
*
* NOTE: Platform, the machine driver, confirms ext clk support
* by registering for the 'ext_mclk_cb' callback with
* Q6AFE driver.
*/
if (clk_priv->supports_ext_mclk) {
ret = afe_set_lpass_clk_cfg_ext_mclk(IDX_RSVD_3,
&clk_priv->clk_cfg,
clk_priv->ext_mclk_freq);
if (ret == 0) {
goto prepare_pinctrl;
} else if (ret == -EOPNOTSUPP) {
pr_err_ratelimited("%s: ext mclk prepare failed; falling back to internal clk\n",
__func__);
} else {
goto err;
}
}
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3,
&clk_priv->clk_cfg);
if (ret < 0)
goto err;
}
prepare_pinctrl:
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->active);
pnctrl_info->active);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
return -EIO;
}
}
if (pnctrl_info->base)
iowrite32(1, pnctrl_info->base);
return 0;
err:
if (__ratelimit(&rtl))
pr_err_ratelimited("%s: afe_set_lpass_clk_cfg_ext_mclk failed with %d\n",
__func__, ret);
return ret;
}
static void audio_ext_clk_unprepare(struct clk_hw *hw)
{
struct audio_ext_clk_priv *clk_priv = to_audio_clk(hw);
struct pinctrl_info *pnctrl_info = &clk_priv->audio_clk.pnctrl_info;
int ret;
int ret = 0;
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if (pnctrl_info->pinctrl) {
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
pnctrl_info->sleep);
if (ret) {
pr_err("%s: active state select failed with %d\n",
__func__, ret);
@ -117,19 +148,34 @@ static void audio_ext_clk_unprepare(struct clk_hw *hw)
}
}
if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) &&
(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
clk_priv->clk_cfg.enable = 0;
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
if (ret < 0) {
if (__ratelimit(&rtl))
pr_err_ratelimited("%s: afe_set_lpass_clk_cfg failed, ret = %d\n",
__func__, ret);
}
}
if (pnctrl_info->base)
iowrite32(0, pnctrl_info->base);
if ((clk_priv->clk_src >= AUDIO_EXT_CLK_LPASS) &&
(clk_priv->clk_src < AUDIO_EXT_CLK_LPASS_MAX)) {
clk_priv->clk_cfg.enable = 0;
if (clk_priv->supports_ext_mclk) {
ret = afe_set_lpass_clk_cfg_ext_mclk(IDX_RSVD_3,
&clk_priv->clk_cfg,
clk_priv->ext_mclk_freq);
if (ret == 0) {
goto exit;
} else if (ret == -EOPNOTSUPP) {
pr_err_ratelimited("%s: ext mclk unprepare failed; falling back to internal clk\n",
__func__);
} else {
goto exit;
}
}
ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk_priv->clk_cfg);
if (ret < 0)
goto exit;
}
exit:
if (ret && __ratelimit(&rtl))
pr_err_ratelimited("%s: afe_set_lpass_clk_cfg failed, ret = %d\n",
__func__, ret);
}
static u8 audio_ext_clk_get_parent(struct clk_hw *hw)
@ -518,6 +564,7 @@ static int audio_ref_clk_probe(struct platform_device *pdev)
int ret;
struct audio_ext_clk_priv *clk_priv;
u32 clk_freq = 0, clk_id = 0, clk_src = 0, use_pinctrl = 0;
u32 ext_mclk_src_freq = 0;
clk_priv = devm_kzalloc(&pdev->dev, sizeof(*clk_priv), GFP_KERNEL);
if (!clk_priv)
@ -561,9 +608,27 @@ static int audio_ref_clk_probe(struct platform_device *pdev)
if (!ret)
clk_priv->clk_cfg.clk_id = clk_id;
if (of_property_read_bool(pdev->dev.of_node,
"qcom,supports-ext-mclk"))
clk_priv->supports_ext_mclk = true;
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,ext-mclk-src-freq",
&ext_mclk_src_freq);
if (!ret) {
clk_priv->ext_mclk_freq = ext_mclk_src_freq;
} else if (clk_priv->supports_ext_mclk) {
dev_err(&pdev->dev, "%s: qcom,ext-mclk-src-freq not defined\n",
__func__);
return ret;
}
dev_dbg(&pdev->dev, "%s: ext-clk freq: %d, lpass clk_id: %d, clk_src: %d\n",
__func__, clk_priv->clk_cfg.clk_freq_in_hz,
clk_priv->clk_cfg.clk_id, clk_priv->clk_src);
__func__, clk_priv->clk_cfg.clk_freq_in_hz,
clk_priv->clk_cfg.clk_id, clk_priv->clk_src);
dev_dbg(&pdev->dev, "%s: supports-ext-mclk: %d, ext-mclk-input-freq: %d\n",
__func__, clk_priv->supports_ext_mclk, clk_priv->ext_mclk_freq);
platform_set_drvdata(pdev, clk_priv);
ret = of_property_read_string(pdev->dev.of_node, "pmic-clock-names",

View file

@ -465,6 +465,7 @@ struct rx_macro_priv {
u16 default_clk_id;
int8_t rx0_gain_val;
int8_t rx1_gain_val;
u32 mclk_freq;
};
static struct snd_soc_dai_driver rx_macro_dai[];
@ -1411,11 +1412,12 @@ static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w,
int ret = 0;
struct device *rx_dev = NULL;
struct rx_macro_priv *rx_priv = NULL;
int mclk_freq = MCLK_FREQ;
int mclk_freq = 0;
if (!rx_macro_get_data(component, &rx_dev, &rx_priv, __func__))
return -EINVAL;
mclk_freq = rx_priv->mclk_freq;
dev_dbg(rx_dev, "%s: event = %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@ -2689,7 +2691,8 @@ static void rx_macro_hphdelay_lutbypass(struct snd_soc_component *component,
}
if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
snd_soc_component_update_bits(component,
if (!rx_priv->is_ear_mode_on)
snd_soc_component_update_bits(component,
BOLERO_CDC_RX_RX0_RX_PATH_CFG1,
0x02, 0x00);
snd_soc_component_update_bits(component, hph_lut_bypass_reg,
@ -4166,13 +4169,14 @@ static int rx_macro_probe(struct platform_device *pdev)
{
struct macro_ops ops = {0};
struct rx_macro_priv *rx_priv = NULL;
u32 rx_base_addr = 0, muxsel = 0;
u32 rx_base_addr = 0, muxsel = 0, mclk_freq = 0;
char __iomem *rx_io_base = NULL, *muxsel_io = NULL;
int ret = 0;
u8 bcl_pmic_params[3];
u32 default_clk_id = 0;
u32 is_used_rx_swr_gpio = 1;
const char *is_used_rx_swr_gpio_dt = "qcom,is-used-swr-gpio";
const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate";
if (!bolero_is_va_macro_registered(&pdev->dev)) {
dev_err(&pdev->dev,
@ -4207,6 +4211,15 @@ static int rx_macro_probe(struct platform_device *pdev)
__func__, "qcom,default-clk-id");
default_clk_id = RX_CORE_CLK;
}
ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate,
&mclk_freq);
if (ret) {
rx_priv->mclk_freq = MCLK_FREQ;
} else {
rx_priv->mclk_freq = mclk_freq;
}
dev_dbg(rx_priv->dev,
"%s: mclk_freq = %u\n", __func__, rx_priv->mclk_freq);
if (of_find_property(pdev->dev.of_node, is_used_rx_swr_gpio_dt,
NULL)) {
ret = of_property_read_u32(pdev->dev.of_node,

View file

@ -186,6 +186,7 @@ struct tx_macro_priv {
bool register_event_listener;
u16 current_clk_id;
int disable_afe_wakeup_event_listener;
u32 mclk_freq;
};
static bool tx_macro_get_data(struct snd_soc_component *component,
@ -2985,13 +2986,12 @@ static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
struct tx_macro_priv *tx_priv)
{
u32 div_factor = TX_MACRO_CLK_DIV_2;
u32 mclk_rate = TX_MACRO_MCLK_FREQ;
if (dmic_sample_rate == TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED ||
mclk_rate % dmic_sample_rate != 0)
tx_priv->mclk_freq % dmic_sample_rate != 0)
goto undefined_rate;
div_factor = mclk_rate / dmic_sample_rate;
div_factor = tx_priv->mclk_freq / dmic_sample_rate;
switch (div_factor) {
case 2:
@ -3019,13 +3019,13 @@ static int tx_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
/* Valid dmic DIV factors */
dev_dbg(tx_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n",
__func__, div_factor, mclk_rate);
__func__, div_factor, tx_priv->mclk_freq);
return dmic_sample_rate;
undefined_rate:
dev_dbg(tx_priv->dev, "%s: Invalid rate %d, for mclk %d\n",
__func__, dmic_sample_rate, mclk_rate);
__func__, dmic_sample_rate, tx_priv->mclk_freq);
dmic_sample_rate = TX_MACRO_DMIC_SAMPLE_RATE_UNDEFINED;
return dmic_sample_rate;
@ -3362,10 +3362,11 @@ static int tx_macro_probe(struct platform_device *pdev)
{
struct macro_ops ops = {0};
struct tx_macro_priv *tx_priv = NULL;
u32 tx_base_addr = 0, sample_rate = 0;
u32 tx_base_addr = 0, sample_rate = 0, mclk_freq = 0;
char __iomem *tx_io_base = NULL;
int ret = 0;
const char *dmic_sample_rate = "qcom,tx-dmic-sample-rate";
const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate";
u32 is_used_tx_swr_gpio = 1;
const char *is_used_tx_swr_gpio_dt = "qcom,is-used-swr-gpio";
u32 disable_afe_wakeup_event_listener = 0;
@ -3425,6 +3426,17 @@ static int tx_macro_probe(struct platform_device *pdev)
return -ENOMEM;
}
tx_priv->tx_io_base = tx_io_base;
ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate,
&mclk_freq);
if (ret) {
tx_priv->mclk_freq = TX_MACRO_MCLK_FREQ;
} else {
tx_priv->mclk_freq = mclk_freq;
}
dev_dbg(tx_priv->dev,
"%s: mclk_freq = %u\n", __func__, tx_priv->mclk_freq);
ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate,
&sample_rate);
if (ret) {

View file

@ -186,6 +186,7 @@ struct va_macro_priv {
u16 current_clk_id;
int pcm_rate[VA_MACRO_NUM_DECIMATORS];
bool dev_up;
u32 mclk_freq;
};
static bool va_macro_get_data(struct snd_soc_component *component,
@ -1567,6 +1568,12 @@ static int va_macro_hw_params(struct snd_pcm_substream *substream,
dai->name, dai->id, params_rate(params),
params_channels(params));
if (va_priv->mclk_freq != VA_MACRO_MCLK_FREQ) {
dev_err(va_dev, "%s: unsupported VA mclk: %u\n",
__func__, va_priv->mclk_freq);
return -EINVAL;
}
sample_rate = params_rate(params);
if (sample_rate > 16000)
va_priv->clk_div_switch = true;
@ -2636,13 +2643,12 @@ static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
struct va_macro_priv *va_priv)
{
u32 div_factor;
u32 mclk_rate = VA_MACRO_MCLK_FREQ;
if (dmic_sample_rate == VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED ||
mclk_rate % dmic_sample_rate != 0)
va_priv->mclk_freq % dmic_sample_rate != 0)
goto undefined_rate;
div_factor = mclk_rate / dmic_sample_rate;
div_factor = va_priv->mclk_freq / dmic_sample_rate;
switch (div_factor) {
case 2:
@ -2670,13 +2676,13 @@ static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate,
/* Valid dmic DIV factors */
dev_dbg(va_priv->dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n",
__func__, div_factor, mclk_rate);
__func__, div_factor, va_priv->mclk_freq);
return dmic_sample_rate;
undefined_rate:
dev_dbg(va_priv->dev, "%s: Invalid rate %d, for mclk %d\n",
__func__, dmic_sample_rate, mclk_rate);
__func__, dmic_sample_rate, va_priv->mclk_freq);
dmic_sample_rate = VA_MACRO_DMIC_SAMPLE_RATE_UNDEFINED;
return dmic_sample_rate;
@ -3061,7 +3067,7 @@ static int va_macro_probe(struct platform_device *pdev)
{
struct macro_ops ops;
struct va_macro_priv *va_priv;
u32 va_base_addr, sample_rate = 0;
u32 va_base_addr, sample_rate = 0, mclk_freq = 0;
char __iomem *va_io_base;
bool va_without_decimation = false;
const char *micb_supply_str = "va-vdd-micb-supply";
@ -3074,6 +3080,7 @@ static int va_macro_probe(struct platform_device *pdev)
struct clk *lpass_audio_hw_vote = NULL;
u32 is_used_va_swr_gpio = 0;
const char *is_used_va_swr_gpio_dt = "qcom,is-used-swr-gpio";
const char *cdc_mclk_clk_rate = "qcom,cdc-mclk-clk-rate";
va_priv = devm_kzalloc(&pdev->dev, sizeof(struct va_macro_priv),
GFP_KERNEL);
@ -3092,6 +3099,15 @@ static int va_macro_probe(struct platform_device *pdev)
"qcom,va-without-decimation");
va_priv->va_without_decimation = va_without_decimation;
ret = of_property_read_u32(pdev->dev.of_node, cdc_mclk_clk_rate,
&mclk_freq);
if (ret) {
va_priv->mclk_freq = VA_MACRO_MCLK_FREQ;
} else {
va_priv->mclk_freq = mclk_freq;
}
dev_dbg(va_priv->dev,
"%s: mclk_freq = %u\n", __func__, va_priv->mclk_freq);
ret = of_property_read_u32(pdev->dev.of_node, dmic_sample_rate,
&sample_rate);
if (ret) {

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/platform_device.h>
#include <linux/slab.h>
@ -32,6 +33,7 @@
enum {
DP_CONTROLLER0 = 0,
HDMI_CONTROLLER,
DP_CONTROLLER_MAX,
};
@ -96,7 +98,7 @@ static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
codec_data->ctl[dai_id], codec_data->stream[dai_id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai_id == HDMI_MS_DAI)
if (dai_id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -148,7 +150,7 @@ static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
codec_data->ctl[dai_id], codec_data->stream[dai_id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai_id == HDMI_MS_DAI)
if (dai_id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -214,7 +216,7 @@ static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
codec_data->ctl[dai_id], codec_data->stream[dai_id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai_id == HDMI_MS_DAI)
if (dai_id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -306,7 +308,7 @@ static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
codec_data->ctl[dai_id], codec_data->stream[dai_id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai_id == HDMI_MS_DAI)
if (dai_id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -366,7 +368,7 @@ static int msm_ext_disp_audio_device_get(struct snd_kcontrol *kcontrol,
int dai_id = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
if (dai_id < 0 || dai_id > DP_DAI2) {
if (dai_id < 0 || dai_id >= DP_DAI_MAX) {
dev_err(component->dev,
"%s: invalid dai id: %d\n", __func__, dai_id);
rc = -EINVAL;
@ -398,7 +400,7 @@ static int msm_ext_disp_audio_device_set(struct snd_kcontrol *kcontrol,
int dai_id = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
if (dai_id < 0 || dai_id > DP_DAI2) {
if (dai_id < 0 || dai_id >= DP_DAI_MAX) {
dev_err(component->dev,
"%s: invalid dai id: %d\n", __func__, dai_id);
rc = -EINVAL;
@ -498,8 +500,8 @@ static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
SND_SOC_NOPM, DP_DAI2, DP_STREAM_MAX - 1, 0, 2,
msm_ext_disp_audio_device_get,
msm_ext_disp_audio_device_set),
SOC_SINGLE_MULTI_EXT("External HDMI Device",
SND_SOC_NOPM, HDMI_MS_DAI, DP_STREAM_MAX - 1, 0, 2,
SOC_SINGLE_MULTI_EXT("External HDMI Audio Device",
SND_SOC_NOPM, HDMI_DAI, DP_STREAM_MAX - 1, 0, 2,
msm_ext_disp_audio_device_get,
msm_ext_disp_audio_device_set),
@ -526,7 +528,7 @@ static int msm_ext_disp_audio_codec_rx_dai_startup(
codec_data->ctl[dai->id], codec_data->stream[dai->id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai->id == HDMI_MS_DAI)
if (dai->id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -587,7 +589,7 @@ static int msm_ext_disp_audio_codec_rx_dai_hw_params(
codec_data->ctl[dai->id], codec_data->stream[dai->id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai->id == HDMI_MS_DAI)
if (dai->id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
@ -661,6 +663,10 @@ static int msm_ext_disp_audio_codec_rx_dai_hw_params(
audio_setup_params.down_mix = down_mix;
mutex_lock(&codec_data->dp_ops_lock);
if (dai->id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;
SWITCH_DP_CODEC(codec_info, codec_data, dai->id, type);
rc = msm_ext_disp_select_audio_codec(codec_data->ext_disp_core_pdev,
&codec_info);
@ -701,7 +707,7 @@ static void msm_ext_disp_audio_codec_rx_dai_shutdown(
codec_data->ctl[dai->id], codec_data->stream[dai->id]);
mutex_lock(&codec_data->dp_ops_lock);
if (dai->id == HDMI_MS_DAI)
if (dai->id == HDMI_DAI)
type = EXT_DISPLAY_TYPE_HDMI;
else
type = EXT_DISPLAY_TYPE_DP;

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
@ -60,6 +60,8 @@ enum {
HPH_COMP_DELAY,
HPH_PA_DELAY,
AMIC2_BCS_ENABLE,
WCD_HPHL_EN,
WCD_EAR_EN,
};
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
@ -876,6 +878,7 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
set_bit(HPH_PA_DELAY, &wcd937x->status_mask);
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_PDM_WD_CTL0, 0x17, 0x13);
set_bit(WCD_HPHL_EN, &wcd937x->status_mask);
break;
case SND_SOC_DAPM_POST_PMU:
/*
@ -906,12 +909,14 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
WCD937X_IRQ_HPHL_PDM_WD_INT);
break;
case SND_SOC_DAPM_PRE_PMD:
wcd_disable_irq(&wcd937x->irq_info,
if (!test_bit(WCD_EAR_EN, &wcd937x->status_mask)) {
wcd_disable_irq(&wcd937x->irq_info,
WCD937X_IRQ_HPHL_PDM_WD_INT);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
SLV_BOLERO_EVT_RX_MUTE,
(WCD_RX1 << 0x10 | 0x1));
}
blocking_notifier_call_chain(&wcd937x->mbhc->notifier,
WCD_EVENT_PRE_HPHL_PA_OFF,
&wcd937x->mbhc->wcd_mbhc);
@ -942,6 +947,7 @@ static int wcd937x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
WCD_CLSH_EVENT_POST_PA,
WCD_CLSH_STATE_HPHL,
hph_mode);
clear_bit(WCD_HPHL_EN, &wcd937x->status_mask);
break;
};
return ret;
@ -1030,10 +1036,12 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_PDM_WD_CTL2,
0x05, 0x05);
else
else {
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_PDM_WD_CTL0,
0x17, 0x13);
set_bit(WCD_EAR_EN, &wcd937x->status_mask);
}
if (!wcd937x->comp1_enable)
snd_soc_component_update_bits(component,
WCD937X_ANA_EAR_COMPANDER_CTL, 0x80, 0x80);
@ -1056,16 +1064,24 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
WCD937X_IRQ_HPHL_PDM_WD_INT);
break;
case SND_SOC_DAPM_PRE_PMD:
if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX)
if (wcd937x->ear_rx_path & EAR_RX_PATH_AUX) {
wcd_disable_irq(&wcd937x->irq_info,
WCD937X_IRQ_AUX_PDM_WD_INT);
else
wcd_disable_irq(&wcd937x->irq_info,
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
SLV_BOLERO_EVT_RX_MUTE,
(WCD_RX1 << 0x10 | 0x1));
}
else {
if(!test_bit(WCD_HPHL_EN, &wcd937x->status_mask)) {
wcd_disable_irq(&wcd937x->irq_info,
WCD937X_IRQ_HPHL_PDM_WD_INT);
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
if (wcd937x->update_wcd_event)
wcd937x->update_wcd_event(wcd937x->handle,
SLV_BOLERO_EVT_RX_MUTE,
(WCD_RX1 << 0x10 | 0x1));
}
}
break;
case SND_SOC_DAPM_POST_PMD:
if (!wcd937x->comp1_enable)
@ -1082,10 +1098,12 @@ static int wcd937x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_PDM_WD_CTL2,
0x05, 0x00);
else
else {
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_PDM_WD_CTL0,
0x17, 0x00);
clear_bit(WCD_EAR_EN, &wcd937x->status_mask);
}
usleep_range(10000, 10010);
/* disable EAR CnP FSM */
snd_soc_component_update_bits(component,
@ -1151,13 +1169,16 @@ static int wcd937x_enable_rx1(struct snd_soc_dapm_widget *w,
wcd937x_rx_connect_port(component, COMP_L, true);
break;
case SND_SOC_DAPM_POST_PMD:
wcd937x_rx_connect_port(component, HPH_L, false);
if (wcd937x->comp1_enable)
wcd937x_rx_connect_port(component, COMP_L, false);
wcd937x_rx_clk_disable(component);
snd_soc_component_update_bits(component,
if (!test_bit(WCD_HPHL_EN, &wcd937x->status_mask) &&
!test_bit(WCD_EAR_EN, &wcd937x->status_mask)) {
wcd937x_rx_connect_port(component, HPH_L, false);
if (wcd937x->comp1_enable)
wcd937x_rx_connect_port(component, COMP_L, false);
wcd937x_rx_clk_disable(component);
snd_soc_component_update_bits(component,
WCD937X_DIGITAL_CDC_DIG_CLK_CTL,
0x01, 0x00);
}
break;
};
return 0;

View file

@ -41,6 +41,7 @@
#include "codecs/bolero/wsa-macro.h"
#include "lahaina-port-config.h"
#include "msm_dailink.h"
#include "msm-common.h"
#define DRV_NAME "lahaina-asoc-snd"
#define __CHIPSET__ "LAHAINA "
@ -132,16 +133,6 @@ enum {
AUX_PCM_MAX,
};
enum {
PRIM_MI2S = 0,
SEC_MI2S,
TERT_MI2S,
QUAT_MI2S,
QUIN_MI2S,
SEN_MI2S,
MI2S_MAX,
};
enum {
WSA_CDC_DMA_RX_0 = 0,
WSA_CDC_DMA_RX_1,
@ -182,28 +173,42 @@ enum {
AFE_LOOPBACK_TX_IDX_MAX,
};
struct msm_asoc_mach_data {
struct snd_info_entry *codec_root;
int usbc_en2_gpio; /* used by gpio driver API */
int lito_v2_enabled;
struct device_node *dmic01_gpio_p; /* used by pinctrl API */
struct device_node *dmic23_gpio_p; /* used by pinctrl API */
struct device_node *dmic45_gpio_p; /* used by pinctrl API */
struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */
atomic_t mi2s_gpio_ref_count[MI2S_MAX]; /* used by pinctrl API */
struct device_node *us_euro_gpio_p; /* used by pinctrl API */
struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */
struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
bool is_afe_config_done;
struct device_node *fsa_handle;
struct clk *lpass_audio_hw_vote;
int core_audio_vote_count;
u32 wsa_max_devs;
u32 tdm_max_slots; /* Max TDM slots used */
int wcd_disabled;
int (*get_wsa_dev_num)(struct snd_soc_component*);
struct afe_cps_hw_intf_cfg cps_config;
#define MCLK_CFG_CELLS 5
struct ext_mclk_freq_cfg {
u32 clk_freq;
u32 div2x;
u32 m;
u32 n;
u32 d;
};
/* Coupled with "qcom,ext-mclk-srcs" DTSI property */
#define MCLK_SRCS_CELLS 3
struct ext_mclk_src_cfg {
u32 clk_id;
u32 clk_root;
u32 gpio_idx;
};
struct ext_mclk_cfg_info {
u32 mclk_freq;
const char *prop;
struct ext_mclk_freq_cfg *mclk_cfg;
u32 num_mclk_cfg;
};
struct ext_mclk_gpio_info {
struct device_node *gpio_p; /* used by pinctrl API */
const char *ext_mclk_muxsel_str;
uint32_t ext_mclk_muxsel_val;
uint32_t ref_cnt;
struct mutex lock;
};
struct ext_mclk_src_info {
u32 clk_id;
u32 clk_root;
struct ext_mclk_gpio_info *gpio_info;
};
struct tdm_port {
@ -227,6 +232,19 @@ struct dev_config {
u32 channels;
};
static bool ext_mclk_enable;
static uint32_t num_ext_mclk_gpios;
static struct ext_mclk_gpio_info *ext_mclk_gpio_info;
static struct ext_mclk_src_cfg *ext_mclk_src_info;
static struct ext_mclk_cfg_info ext_mclk_freq_info[MCLK_FREQ_MAX] = {
[MCLK_FREQ_11P2896_MHZ] = {11289600, "ext-mclk-cfg-11p2896", NULL, 0},
[MCLK_FREQ_12P288_MHZ] = {12288000, "ext-mclk-cfg-12p288", NULL, 0},
[MCLK_FREQ_16P384_MHZ] = {16384000, "ext-mclk-cfg-16p384", NULL, 0},
[MCLK_FREQ_19P200_MHZ] = {19200000, "ext-mclk-cfg-19p200", NULL, 0},
[MCLK_FREQ_22P5792_MHZ] = {22579200, "ext-mclk-cfg-22p5792", NULL, 0},
[MCLK_FREQ_24P576_MHZ] = {24576000, "ext-mclk-cfg-24p576", NULL, 0},
};
/* Default configuration of slimbus channels */
static struct dev_config slim_rx_cfg[] = {
[SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
@ -1027,6 +1045,213 @@ static cpumask_t audio_cpu_map = CPU_MASK_NONE;
static struct dev_pm_qos_request *msm_audio_req;
static unsigned int qos_client_active_cnt;
static int lahaina_audio_vote(struct snd_soc_card *card, bool enable)
{
struct msm_asoc_mach_data *pdata = NULL;
int ret = 0;
if (!card) {
pr_err("%s: sound card is NULL\n", __func__);
return -EINVAL;
}
pdata = snd_soc_card_get_drvdata(card);
if (!pdata || !pdata->lpass_audio_hw_vote) {
pr_err("%s: lpass audio hw voting not supported\n", __func__);
return -EINVAL;
}
/* Locking and reference counting is handled by the underlying clock
* framework.
*/
if (enable) {
ret = clk_prepare_enable(pdata->lpass_audio_hw_vote);
if (ret < 0) {
dev_err(card->dev, "%s: audio vote error: %d\n",
__func__, ret);
return ret;
}
} else {
clk_disable_unprepare(pdata->lpass_audio_hw_vote);
}
return ret;
}
static int lahaina_populate_ext_mclk_cfg(struct ext_mclk_cfg_info *freq_cfg,
uint32_t mclk_freq,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg)
{
struct ext_mclk_freq_cfg *mclk_cfg = NULL;
uint32_t mclk_cfg_entries = 0;
enum afe_mclk_freq freq = MCLK_FREQ_MIN;
int i = 0;
if (!freq_cfg || !dyn_mclk_cfg)
return -EINVAL;
for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) {
if (freq_cfg[freq].mclk_freq == mclk_freq)
break;
}
if (freq == MCLK_FREQ_MAX) {
pr_err("%s: unsupported mclk freq: %u\n", __func__, mclk_freq);
return -EINVAL;
}
if (!freq_cfg[freq].mclk_cfg ||
!freq_cfg[freq].num_mclk_cfg) {
pr_err("%s: cfg table unavailable for mclk freq: %u\n",
__func__, mclk_freq);
return -EINVAL;
}
mclk_cfg = freq_cfg[freq].mclk_cfg;
mclk_cfg_entries = freq_cfg[freq].num_mclk_cfg;
for (i = 0; i < mclk_cfg_entries; i++) {
if (mclk_cfg[i].clk_freq == dyn_mclk_cfg->clk_freq_in_hz) {
dyn_mclk_cfg->divider_2x = mclk_cfg[i].div2x;
dyn_mclk_cfg->m = mclk_cfg[i].m;
dyn_mclk_cfg->n = mclk_cfg[i].n;
dyn_mclk_cfg->d = mclk_cfg[i].d;
break;
}
}
if (i == mclk_cfg_entries) {
pr_err("%s: requested output mclk freq %u is not supported\n",
__func__, dyn_mclk_cfg->clk_freq_in_hz);
return -EINVAL;
}
return 0;
}
static int lahaina_handle_ext_mclk_gpio(struct snd_soc_card *card,
struct ext_mclk_gpio_info *gpio_info,
uint32_t enable)
{
int ret = 0;
if (!gpio_info)
return -EINVAL;
mutex_lock(&gpio_info->lock);
if (enable) {
if (++gpio_info->ref_cnt == 1) {
ret = msm_cdc_pinctrl_select_active_state(gpio_info->gpio_p);
if (ret) {
dev_err(card->dev, "%s: couldn't activate mclk pinctrl\n",
__func__);
goto unlock;
}
if (gpio_info->ext_mclk_muxsel_str != NULL) {
ret = lahaina_audio_vote(card, true);
if (ret) {
dev_err(card->dev, "%s: HW voting failed, ret: %d\n",
__func__, ret);
goto unlock;
}
ret = afe_set_lpass_ext_mclk_mux_cfg(gpio_info->ext_mclk_muxsel_str,
gpio_info->ext_mclk_muxsel_val);
lahaina_audio_vote(card, false);
}
}
} else {
if (--gpio_info->ref_cnt == 0) {
ret = msm_cdc_pinctrl_select_sleep_state(gpio_info->gpio_p);
if (ret) {
dev_err(card->dev, "%s: couldn't deactivate mclk pinctrl\n",
__func__);
}
}
}
unlock:
mutex_unlock(&gpio_info->lock);
if (ret)
enable ? --gpio_info->ref_cnt : ++gpio_info->ref_cnt;
return ret;
}
static int lahaina_enable_and_get_mclk_cfg(void *private_data, uint32_t enable,
uint32_t mclk_freq,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg)
{
struct snd_soc_card *card = (struct snd_soc_card *)private_data;
struct msm_asoc_mach_data *pdata = NULL;
struct ext_mclk_src_info *ext_mclk_src = NULL;
int i = 0;
int ret = 0;
if (!card || !dyn_mclk_cfg)
return -EINVAL;
if (!ext_mclk_enable) {
dev_err(card->dev, "%s: ext mclk support not enabled on the platform\n",
__func__);
return -EOPNOTSUPP;
}
pdata = snd_soc_card_get_drvdata(card);
if (!pdata || !pdata->ext_mclk_srcs || !pdata->num_ext_mclk_srcs)
return -EINVAL;
for (i = 0; i < pdata->num_ext_mclk_srcs; i++) {
if (pdata->ext_mclk_srcs[i].clk_id == dyn_mclk_cfg->clk_id)
break;
}
if (i == pdata->num_ext_mclk_srcs) {
dev_err(card->dev, "%s: unsupported clk id for ext mclk support: %u\n",
__func__, dyn_mclk_cfg->clk_id);
return -EINVAL;
}
ext_mclk_src = &pdata->ext_mclk_srcs[i];
if (!ext_mclk_src) {
dev_err(card->dev, "%s: ext mclk src/clk cfg unavailable for mclk: %u\n",
__func__, pdata->ext_mclk_srcs[i].clk_id);
return -EINVAL;
}
/* Populate clk root */
dyn_mclk_cfg->clk_root = (uint16_t) ext_mclk_src->clk_root;
/* Populate div2x, M, N, D values */
ret = lahaina_populate_ext_mclk_cfg(ext_mclk_freq_info, mclk_freq,
dyn_mclk_cfg);
if (ret) {
dev_err(card->dev, "%s: unable to populate ext mclk cfg, ret: %d\n", __func__, ret);
goto reset;
}
/* Enable/disable ext mclk GPIO */
ret = lahaina_handle_ext_mclk_gpio(card, ext_mclk_src->gpio_info, enable);
if (ret) {
dev_err(card->dev, "%s: unable to enable/disable ext mclk gpio, ret: %d\n",
__func__, ret);
goto reset;
}
return 0;
reset:
/* Reset clk cfg */
dyn_mclk_cfg->divider_2x = 0;
dyn_mclk_cfg->m = 0;
dyn_mclk_cfg->n = 0;
dyn_mclk_cfg->d = 0;
dyn_mclk_cfg->clk_root = 0;
return ret;
}
static void msm_audio_add_qos_request(void)
{
int i;
@ -1814,20 +2039,41 @@ static int tdm_get_sample_rate(int value)
sample_rate = SAMPLING_RATE_8KHZ;
break;
case 1:
sample_rate = SAMPLING_RATE_16KHZ;
sample_rate = SAMPLING_RATE_11P025KHZ;
break;
case 2:
sample_rate = SAMPLING_RATE_32KHZ;
sample_rate = SAMPLING_RATE_16KHZ;
break;
case 3:
sample_rate = SAMPLING_RATE_48KHZ;
sample_rate = SAMPLING_RATE_22P05KHZ;
break;
case 4:
sample_rate = SAMPLING_RATE_176P4KHZ;
sample_rate = SAMPLING_RATE_32KHZ;
break;
case 5:
sample_rate = SAMPLING_RATE_44P1KHZ;
break;
case 6:
sample_rate = SAMPLING_RATE_48KHZ;
break;
case 7:
sample_rate = SAMPLING_RATE_88P2KHZ;
break;
case 8:
sample_rate = SAMPLING_RATE_96KHZ;
break;
case 9:
sample_rate = SAMPLING_RATE_176P4KHZ;
break;
case 10:
sample_rate = SAMPLING_RATE_192KHZ;
break;
case 11:
sample_rate = SAMPLING_RATE_352P8KHZ;
break;
case 12:
sample_rate = SAMPLING_RATE_384KHZ;
break;
default:
sample_rate = SAMPLING_RATE_48KHZ;
break;
@ -1843,23 +2089,44 @@ static int tdm_get_sample_rate_val(int sample_rate)
case SAMPLING_RATE_8KHZ:
sample_rate_val = 0;
break;
case SAMPLING_RATE_16KHZ:
case SAMPLING_RATE_11P025KHZ:
sample_rate_val = 1;
break;
case SAMPLING_RATE_32KHZ:
case SAMPLING_RATE_16KHZ:
sample_rate_val = 2;
break;
case SAMPLING_RATE_48KHZ:
case SAMPLING_RATE_22P05KHZ:
sample_rate_val = 3;
break;
case SAMPLING_RATE_176P4KHZ:
case SAMPLING_RATE_32KHZ:
sample_rate_val = 4;
break;
case SAMPLING_RATE_352P8KHZ:
case SAMPLING_RATE_44P1KHZ:
sample_rate_val = 5;
break;
case SAMPLING_RATE_48KHZ:
sample_rate_val = 6;
break;
case SAMPLING_RATE_88P2KHZ:
sample_rate_val = 7;
break;
case SAMPLING_RATE_96KHZ:
sample_rate_val = 8;
break;
case SAMPLING_RATE_176P4KHZ:
sample_rate_val = 9;
break;
case SAMPLING_RATE_192KHZ:
sample_rate_val = 10;
break;
case SAMPLING_RATE_352P8KHZ:
sample_rate_val = 11;
break;
case SAMPLING_RATE_384KHZ:
sample_rate_val = 12;
break;
default:
sample_rate_val = 3;
sample_rate_val = 6;
break;
}
return sample_rate_val;
@ -4742,6 +5009,25 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
aux_pcm_tx_sample_rate_put),
};
static int msm_ext_mclk_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = ext_mclk_enable;
return 0;
}
static int msm_ext_mclk_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ext_mclk_enable = (bool) ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_kcontrol_new msm_ext_mclk_controls[] = {
SOC_SINGLE_BOOL_EXT("EXT MCLK Enable", 0,
msm_ext_mclk_get, msm_ext_mclk_put),
};
static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
{
int idx;
@ -8806,6 +9092,411 @@ static void parse_cps_configuration(struct platform_device *pdev,
}
}
static int msm_parse_ext_mclk_gpios(struct snd_soc_card *card,
struct ext_mclk_gpio_info **ext_mclk_gpios)
{
int ret = 0;
uint32_t len = 0;
uint32_t num_gpios = 0;
struct device_node *np = NULL;
struct ext_mclk_gpio_info *gpio_info = NULL;
const char *ext_mclk_muxsel_str = NULL;
u32 ext_mclk_muxsel_val = 0;
int i = 0;
if (!card || !card->dev || !card->dev->of_node)
return -EINVAL;
np = card->dev->of_node;
if (!of_get_property(np, "qcom,ext-mclk-gpios", &len)) {
dev_err(card->dev, "%s: ext mclk gpios not found in DT\n", __func__);
return -EINVAL;
}
if (!len) {
dev_err(card->dev, "%s: invalid ext mclk gpios configuration in DT\n",
__func__);
return -EINVAL;
}
num_gpios = len / sizeof(uint32_t);
dev_dbg(card->dev, "%d ext mclk gpios found\n", num_gpios);
gpio_info = devm_kzalloc(card->dev,
num_gpios * sizeof(struct ext_mclk_gpio_info),
GFP_KERNEL);
if (!gpio_info)
return -ENOMEM;
for (i = 0; i < num_gpios; i++) {
mutex_init(&gpio_info[i].lock);
gpio_info[i].ref_cnt = 0;
gpio_info[i].gpio_p = of_parse_phandle(np, "qcom,ext-mclk-gpios", i);
if (!gpio_info[i].gpio_p) {
dev_err(card->dev, "ext mclk gpio %d device node is NULL", i);
ret = -EINVAL;
goto free_gpio_info;
}
/* aud_ref_mux is present only for LPI GPIOs on lahaina.
* Hence we mandate parsing of mux config only for LPI GPIOs.
* Review the existence of aud_ref_mux for LPI/TLMM GPIOs
* while porting this change to other platforms
*/
if (of_property_read_bool(gpio_info[i].gpio_p, "qcom,lpi-gpios")) {
ret = of_property_read_string(gpio_info[i].gpio_p,
"qcom,ext-mclk-muxsel-str", &ext_mclk_muxsel_str);
if (ret) {
dev_err(card->dev, "%s: qcom,ext-mclk-muxsel-str not found in DT\n",
__func__);
ret = -EINVAL;
goto free_gpio_info;
} else {
ret = of_property_read_u32(gpio_info[i].gpio_p,
"qcom,ext-mclk-muxsel-val", &ext_mclk_muxsel_val);
if (ret) {
dev_err(card->dev, "%s: qcom,ext-mclk-muxsel-val not found in DT\n",
__func__);
ret = -EINVAL;
goto free_gpio_info;
}
}
dev_dbg(card->dev, "%s: gpio_info[%d] - muxsel= %s, val= %u\n",
__func__, i, ext_mclk_muxsel_str, ext_mclk_muxsel_val);
gpio_info[i].ext_mclk_muxsel_str = ext_mclk_muxsel_str;
gpio_info[i].ext_mclk_muxsel_val = ext_mclk_muxsel_val;
}
}
num_ext_mclk_gpios = num_gpios;
*ext_mclk_gpios = gpio_info;
dev_dbg(card->dev, "%s: ext mclk gpios probe successful\n", __func__);
return 0;
free_gpio_info:
for (; i >= 0; i--) {
mutex_destroy(&gpio_info[i].lock);
of_node_put(gpio_info[i].gpio_p);
}
devm_kfree(card->dev, gpio_info);
gpio_info = NULL;
*ext_mclk_gpios = NULL;
return ret;
}
static int msm_parse_ext_mclk_srcs(struct snd_soc_card *card,
struct ext_mclk_src_cfg **ext_mclk_src,
uint32_t *n_srcs)
{
int ret = 0;
struct ext_mclk_src_cfg *src_cfg = NULL;
struct device_node *np = NULL;
uint32_t len = 0;
uint32_t num_srcs = 0;
uint32_t cells = 0;
if (!card || !card->dev || !card->dev->of_node)
return -EINVAL;
np = card->dev->of_node;
if (!of_get_property(np, "qcom,ext-mclk-srcs", &len)) {
dev_err(card->dev, "%s: ext mclk srcs cfg not found in DT\n", __func__);
return -EINVAL;
}
ret = of_property_read_u32(np, "#ext-mclk-srcs-cells", &cells);
if (ret) {
dev_err(card->dev, "%s: ext mclk srcs cells not found in DT\n", __func__);
return ret;
}
if (!len || (len % (cells * sizeof(uint32_t))) ||
(cells != MCLK_SRCS_CELLS)) {
dev_err(card->dev, "%s: invalid ext mclk srcs cfg in DT\n", __func__);
return -EINVAL;
};
num_srcs = len / (cells * sizeof(uint32_t));
src_cfg = devm_kzalloc(card->dev,
num_srcs * sizeof(struct ext_mclk_src_cfg),
GFP_KERNEL);
if (!src_cfg)
return -ENOMEM;
ret = of_property_read_u32_array(np, "qcom,ext-mclk-srcs", (u32 *)src_cfg,
cells * num_srcs);
if (ret) {
dev_err(card->dev, "%s: could not find %s entry in dt\n",
__func__, "qcom,ext-mclk-srcs");
ret = -EINVAL;
goto free_mclk_array;
}
*ext_mclk_src = src_cfg;
*n_srcs = num_srcs;
dev_dbg(card->dev, "%s: ext mclk srcs probe successful\n", __func__);
return 0;
free_mclk_array:
devm_kfree(card->dev, src_cfg);
src_cfg = NULL;
*ext_mclk_src = NULL;
*n_srcs = 0;
return ret;
}
static int msm_parse_dt_ext_mclk_cfg(struct snd_soc_card *card,
enum afe_mclk_freq freq)
{
int ret = 0;
struct ext_mclk_freq_cfg *mclk_cfg = NULL;
uint32_t len = 0;
uint32_t num_cfg = 0;
uint32_t cells = 0;
int i = 0;
struct device_node *np = NULL;
uint32_t *freq_cfg_tbl = NULL;
if (!card || !card->dev || !card->dev->of_node)
return -EINVAL;
np = card->dev->of_node;
if (!of_get_property(np, ext_mclk_freq_info[freq].prop, &len)) {
dev_dbg(card->dev, "%s: ext mclk cfg for %s not found in DT\n",
__func__, ext_mclk_freq_info[freq].prop);
return 0;
}
ret = of_property_read_u32(np, "#ext-mclk-cfg-cells", &cells);
if (ret) {
dev_err(card->dev, "%s: ext mclk cfg cells not found in DT\n", __func__);
return ret;
}
if (!len || (len % (cells * sizeof(uint32_t))) ||
(cells != MCLK_CFG_CELLS)) {
dev_err(card->dev, "%s: invalid mclk configuration in DT\n", __func__);
return -EINVAL;
};
num_cfg = len / (cells * sizeof(uint32_t));
mclk_cfg = devm_kzalloc(card->dev, num_cfg * sizeof(struct ext_mclk_freq_cfg),
GFP_KERNEL);
if (!mclk_cfg)
return -ENOMEM;
freq_cfg_tbl = devm_kzalloc(card->dev, cells * num_cfg * sizeof(uint32_t),
GFP_KERNEL);
if (!freq_cfg_tbl) {
ret = -ENOMEM;
goto free_mclk_cfg;
}
ret = of_property_read_u32_array(np, ext_mclk_freq_info[freq].prop,
freq_cfg_tbl, cells * num_cfg);
if (ret)
goto free_freq_cfg_tbl;
dev_dbg(card->dev, "table for %u freq\n",
ext_mclk_freq_info[freq].mclk_freq);
for (i = 0; i < num_cfg; i++) {
memcpy(&mclk_cfg[i], &freq_cfg_tbl[i * cells],
sizeof(uint32_t) * cells);
dev_dbg(card->dev,
"clk freq (Hz): %u, div2x: %u, m: %u, n: %u, d: %u\n",
mclk_cfg[i].clk_freq, mclk_cfg[i].div2x, mclk_cfg[i].m,
mclk_cfg[i].n, mclk_cfg[i].d);
}
ext_mclk_freq_info[freq].mclk_cfg = mclk_cfg;
ext_mclk_freq_info[freq].num_mclk_cfg = num_cfg;
devm_kfree(card->dev, freq_cfg_tbl);
freq_cfg_tbl = NULL;
return 0;
free_freq_cfg_tbl:
devm_kfree(card->dev, freq_cfg_tbl);
freq_cfg_tbl = NULL;
free_mclk_cfg:
devm_kfree(card->dev, mclk_cfg);
mclk_cfg = NULL;
return ret;
}
static int msm_parse_ext_mclk_freq_cfg(struct snd_soc_card *card)
{
int ret = 0;
int i = MCLK_FREQ_MIN;
if (!card || !card->dev || !card->dev->of_node)
return -EINVAL;
for (i = MCLK_FREQ_MIN; i < MCLK_FREQ_MAX; i++) {
ret = msm_parse_dt_ext_mclk_cfg(card, i);
if (ret < 0)
return ret;
}
dev_dbg(card->dev, "%s: ext mclk freq probe successful!\n", __func__);
return ret;
}
static void lahaina_ext_mclk_cfg_deinit(struct snd_soc_card *card)
{
struct msm_asoc_mach_data *pdata = NULL;
int i = 0;
enum afe_mclk_freq freq = MCLK_FREQ_MIN;
if (!card || !card->dev)
return;
pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card);
if (!pdata)
return;
afe_unregister_ext_mclk_cb();
for (i = 0; i < pdata->num_ext_mclk_srcs; i++)
pdata->ext_mclk_srcs[i].gpio_info = NULL;
pdata->num_ext_mclk_srcs = 0;
if (ext_mclk_gpio_info) {
for (i = 0; i < num_ext_mclk_gpios; i++) {
mutex_destroy(&ext_mclk_gpio_info[i].lock);
of_node_put(ext_mclk_gpio_info[i].gpio_p);
}
devm_kfree(card->dev, ext_mclk_gpio_info);
ext_mclk_gpio_info = NULL;
}
if (ext_mclk_src_info) {
devm_kfree(card->dev, ext_mclk_src_info);
ext_mclk_src_info = NULL;
}
for (freq = MCLK_FREQ_MIN; freq < MCLK_FREQ_MAX; freq++) {
if (ext_mclk_freq_info[freq].mclk_cfg)
devm_kfree(card->dev, ext_mclk_freq_info[freq].mclk_cfg);
ext_mclk_freq_info[freq].mclk_cfg = NULL;
ext_mclk_freq_info[freq].num_mclk_cfg = 0;
}
}
static int lahaina_ext_mclk_init_controls(struct snd_soc_card *card)
{
int ret = 0;
if (!card) {
pr_err("%s: sound card is NULL\n", __func__);
return -EINVAL;
}
ret = snd_soc_add_card_controls(card, msm_ext_mclk_controls,
ARRAY_SIZE(msm_ext_mclk_controls));
if (ret < 0) {
dev_err(card->dev, "%s: add_card_controls failed for ext mclk ctls: %d\n",
__func__, ret);
return ret;
}
return ret;
}
static int lahaina_ext_mclk_cfg_init(struct snd_soc_card *card)
{
int ret = 0;
struct msm_asoc_mach_data *pdata = NULL;
uint32_t num_ext_mclk_srcs = 0;
int i = 0;
if (!card || !card->dev || !card->dev->of_node)
return -EINVAL;
pdata = (struct msm_asoc_mach_data *) snd_soc_card_get_drvdata(card);
if (!pdata)
return -EINVAL;
ret = msm_parse_ext_mclk_gpios(card, &ext_mclk_gpio_info);
if (ret) {
dev_err(card->dev, "%s: unable to parse ext mclk gpios, ret: %d\n",
__func__, ret);
goto deinit;
}
ret = msm_parse_ext_mclk_srcs(card, &ext_mclk_src_info,
&num_ext_mclk_srcs);
if (ret) {
dev_err(card->dev, "%s: unable to parse ext mclk srcs, ret: %d\n",
__func__, ret);
goto deinit;
}
ret = msm_parse_ext_mclk_freq_cfg(card);
if (ret) {
dev_err(card->dev, "%s: unable to parse ext mclk freq cfg, ret: %d\n",
__func__, ret);
goto deinit;
}
pdata->num_ext_mclk_srcs = num_ext_mclk_srcs;
if (pdata->num_ext_mclk_srcs) {
pdata->ext_mclk_srcs = devm_kzalloc(card->dev,
num_ext_mclk_srcs * sizeof(struct ext_mclk_src_info),
GFP_KERNEL);
if (!pdata->ext_mclk_srcs) {
pdata->num_ext_mclk_srcs = 0;
ret = -ENOMEM;
goto deinit;
}
for (i = 0; i < num_ext_mclk_srcs; i++) {
pdata->ext_mclk_srcs[i].clk_id = ext_mclk_src_info[i].clk_id;
pdata->ext_mclk_srcs[i].clk_root = ext_mclk_src_info[i].clk_root;
pdata->ext_mclk_srcs[i].gpio_info = &ext_mclk_gpio_info[ext_mclk_src_info[i].gpio_idx];
dev_dbg(card->dev, "%s: clk id: 0x%x clk root: 0x%x gpio idx: %d\n",
__func__, pdata->ext_mclk_srcs[i].clk_id,
pdata->ext_mclk_srcs[i].clk_root,
ext_mclk_src_info[i].gpio_idx);
}
ret = lahaina_ext_mclk_init_controls(card);
if (ret) {
dev_err(card->dev, "%s: Could not init ext mclk mixer ctls, ret: %d\n",
__func__, ret);
goto deinit;
}
ret = afe_register_ext_mclk_cb(lahaina_enable_and_get_mclk_cfg,
(void *)card);
if (ret) {
dev_err(card->dev, "%s: Could not register afe ext mclk cb, ret: %d\n",
__func__, ret);
goto deinit;
}
}
dev_dbg(card->dev, "%s: ext mclk init successful!\n", __func__);
return 0;
deinit:
lahaina_ext_mclk_cfg_deinit(card);
return ret;
}
static int msm_asoc_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
@ -8864,15 +9555,15 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
goto err;
}
/* Get maximum WSA device count for this platform */
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,wsa-max-devs", &pdata->wsa_max_devs);
if (ret) {
dev_info(&pdev->dev,
"%s: wsa-max-devs property missing in DT %s, ret = %d\n",
__func__, pdev->dev.of_node->full_name, ret);
pdata->wsa_max_devs = 0;
}
/* Get maximum WSA device count for this platform */
ret = of_property_read_u32(pdev->dev.of_node,
"qcom,wsa-max-devs", &pdata->wsa_max_devs);
if (ret) {
dev_info(&pdev->dev,
"%s: wsa-max-devs property missing in DT %s, ret = %d\n",
__func__, pdev->dev.of_node->full_name, ret);
pdata->wsa_max_devs = 0;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret == -EPROBE_DEFER) {
@ -8887,6 +9578,19 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s: Sound card %s registered\n",
__func__, card->name);
/* Enable ext clk support ONLY after sound card enumeration and
* registration has occurred with the internal clock
*/
if (of_property_read_bool(pdev->dev.of_node, "qcom,supports-ext-mclk")) {
ret = lahaina_ext_mclk_cfg_init(card);
if (ret) {
dev_err(&pdev->dev, "%s: ext mclk cfg init from DT failed: %d\n",
__func__, ret);
goto err;
}
pdata->supports_ext_mclk = 1;
}
ret = of_property_read_u32(pdev->dev.of_node, "qcom,tdm-max-slots",
&pdata->tdm_max_slots);
if (ret) {
@ -9029,7 +9733,11 @@ err:
static int msm_asoc_machine_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct msm_asoc_mach_data *pdata = NULL;
pdata = snd_soc_card_get_drvdata(card);
if (pdata && pdata->supports_ext_mclk)
lahaina_ext_mclk_cfg_deinit(card);
snd_event_master_deregister(&pdev->dev);
snd_soc_unregister_card(card);
msm_i2s_auxpcm_deinit();

View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include "msm-common.h"
int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
int rc = 0;
if (enable) {
if (pdata->lpass_audio_hw_vote == NULL) {
dev_err(rtd->card->dev, "%s: Invalid lpass audio hw node\n",
__func__);
rc = -EINVAL;
goto rtn;
}
if (pdata->core_audio_vote_count == 0) {
rc = clk_prepare_enable(pdata->lpass_audio_hw_vote);
if (rc < 0) {
dev_err(rtd->card->dev, "%s: audio vote error\n",
__func__);
goto rtn;
}
}
pdata->core_audio_vote_count++;
} else {
if (pdata->lpass_audio_hw_vote != NULL) {
if (--pdata->core_audio_vote_count == 0) {
clk_disable_unprepare(
pdata->lpass_audio_hw_vote);
} else if (pdata->core_audio_vote_count < 0) {
pr_err("%s: audio vote mismatch\n", __func__);
pdata->core_audio_vote_count = 0;
}
} else {
pr_err("%s: Invalid lpass audio hw node\n", __func__);
}
}
rtn:
return rc;
}
EXPORT_SYMBOL(msm_lpass_audio_hw_vote_req);

View file

@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _MSM_COMMON_H_
#define _MSM_COMMON_H_
#include <sound/soc.h>
#include <sound/pcm.h>
#include <dsp/apr_audio-v2.h>
enum {
PRIM_MI2S = 0,
SEC_MI2S,
TERT_MI2S,
QUAT_MI2S,
QUIN_MI2S,
SEN_MI2S,
MI2S_MAX,
};
struct msm_asoc_mach_data {
struct snd_info_entry *codec_root;
int usbc_en2_gpio; /* used by gpio driver API */
int lito_v2_enabled;
struct device_node *dmic01_gpio_p; /* used by pinctrl API */
struct device_node *dmic23_gpio_p; /* used by pinctrl API */
struct device_node *dmic45_gpio_p; /* used by pinctrl API */
struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */
atomic_t mi2s_gpio_ref_count[MI2S_MAX]; /* used by pinctrl API */
struct device_node *us_euro_gpio_p; /* used by pinctrl API */
struct pinctrl *usbc_en2_gpio_p; /* used by pinctrl API */
struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
bool supports_ext_mclk;
struct ext_mclk_src_info *ext_mclk_srcs;
u32 num_ext_mclk_srcs;
bool is_afe_config_done;
struct device_node *fsa_handle;
struct clk *lpass_audio_hw_vote;
int core_audio_vote_count;
u32 wsa_max_devs;
u32 tdm_max_slots; /* Max TDM slots used */
int wcd_disabled;
int (*get_wsa_dev_num)(struct snd_soc_component*);
struct afe_cps_hw_intf_cfg cps_config;
};
int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable);
#endif

View file

@ -1,39 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of Qualcomm Innovation Center, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
@ -5219,8 +5186,13 @@ static int msm_compr_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
if (prtd && prtd->audio_client) {
stream_id = prtd->audio_client->session;
be_id = chmixer_pspd->port_idx;
#ifdef CONFIG_PLATFORM_AUTO
msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id,
stream_id, session_type, chmixer_pspd);
#else
msm_pcm_routing_set_channel_mixer_runtime(be_id,
stream_id, session_type, chmixer_pspd);
#endif
}
}

View file

@ -22,6 +22,7 @@
enum {
DP_CONTROLLER0 = 0,
DP_CONTROLLER1,
HDMI_CONTROLLER,
DP_CONTROLLER_MAX,
};

View file

@ -49,7 +49,18 @@
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
#define SAMPLING_RATE_11P025KHZ 11025
#define SAMPLING_RATE_22P05KHZ 22050
#define SAMPLING_RATE_44P1KHZ 44100
#define SAMPLING_RATE_88P2KHZ 88200
#define SAMPLING_RATE_176P4KHZ 176400
#define SAMPLING_RATE_352P8KHZ 352800
#define IS_TDM_INTERFACE(x) \
((x >= AFE_PORT_ID_TDM_PORT_RANGE_START) && (x < AFE_PORT_ID_TDM_PORT_RANGE_END))
static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id);
int msm_lpass_audio_hw_vote_req(struct snd_pcm_substream *substream, bool enable);
enum {
ENC_FMT_NONE,
@ -300,6 +311,11 @@ enum {
IDX_GROUP_TDM_MAX,
};
#define IS_FRACTIONAL(x) \
((x == SAMPLING_RATE_11P025KHZ) || (x == SAMPLING_RATE_22P05KHZ) || \
(x == SAMPLING_RATE_44P1KHZ) || (x == SAMPLING_RATE_88P2KHZ) || \
(x == SAMPLING_RATE_176P4KHZ) || (x == SAMPLING_RATE_352P8KHZ))
struct msm_dai_q6_dai_data {
DECLARE_BITMAP(status_mask, STATUS_MAX);
DECLARE_BITMAP(hwfree_status, STATUS_MAX);
@ -11665,6 +11681,9 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream,
struct msm_dai_q6_tdm_dai_data *dai_data =
dev_get_drvdata(dai->dev);
u16 group_id = dai_data->group_cfg.tdm_cfg.group_id;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
int index = rtd->cpu_dai->id;
int sample_rate = dai_data->rate;
int group_idx = 0;
atomic_t *group_ref = NULL;
int intf_idx = PORT_ID_TO_INTF_IDX(dai->id);
@ -11688,6 +11707,15 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream,
group_ref = &tdm_group_ref[group_idx];
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate))) {
rc = msm_lpass_audio_hw_vote_req(substream, true);
if (rc < 0) {
dev_err(dai->dev, "%s: fail to enable audio hw clk 0x%x\n",
__func__, dai->id);
goto rtn;
}
}
if (msm_dai_q6_get_tdm_clk_ref(group_idx) == 0) {
/* TX and RX share the same clk. So enable the clk
* per TDM interface. */
@ -11746,6 +11774,9 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream,
msm_dai_q6_tdm_set_clk(dai_data,
dai->id, false);
}
if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate)))
msm_lpass_audio_hw_vote_req(substream, false);
dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n",
__func__, dai->id);
} else {
@ -11775,6 +11806,10 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream,
int group_idx = 0;
atomic_t *group_ref = NULL;
int intf_idx = PORT_ID_TO_INTF_IDX(dai->id);
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int index = cpu_dai->id;
int sample_rate = dai_data->rate;
group_idx = msm_dai_q6_get_group_idx(dai->id);
if (group_idx < 0) {
@ -11831,6 +11866,9 @@ static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream,
}
if (IS_TDM_INTERFACE(index) && (IS_FRACTIONAL(sample_rate)))
msm_lpass_audio_hw_vote_req(substream, false);
mutex_unlock(&tdm_mutex);
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/init.h>
#include <linux/err.h>
@ -879,6 +879,13 @@ static int msm_lsm_dereg_model(struct snd_pcm_substream *substream,
if (p_info->model_id != 0 &&
p_info->param_type == LSM_DEREG_MULTI_SND_MODEL) {
if(list_empty(&client->stage_cfg[p_info->stage_idx].sound_models)) {
pr_err("%s: sound_models list is empty \n",
__func__);
return -EINVAL;
}
list_for_each_entry(sm,
&client->stage_cfg[p_info->stage_idx].sound_models,
list) {
@ -2379,8 +2386,13 @@ static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
prtd->lsm_client->get_param_payload = NULL;
goto done;
}
if (__builtin_uadd_overflow(sizeof(p_info_32), p_info_32.param_size, &size)) {
pr_err("%s: param size exceeds limit of %u bytes.\n",
__func__, UINT_MAX);
err = -EINVAL;
goto done;
}
size = sizeof(p_info_32) + p_info_32.param_size;
param_info_rsp = kzalloc(size, GFP_KERNEL);
if (!param_info_rsp) {

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/*
* Add support for 24 and 32bit format for ASM loopback and playback session.
@ -1022,10 +1022,17 @@ static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
if (prtd->audio_client) {
stream_id = prtd->audio_client->session;
be_id = chmixer_pspd->port_idx;
#ifdef CONFIG_PLATFORM_AUTO
msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id,
stream_id,
session_type,
chmixer_pspd);
#else
msm_pcm_routing_set_channel_mixer_runtime(be_id,
stream_id,
session_type,
chmixer_pspd);
#endif
}
}
mutex_unlock(&loopback_session_lock);

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/*
* Add support for 24 and 32bit format for ASM loopback and playback session.
@ -1329,7 +1329,7 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
offset = prtd->in_frame_info[idx].offset;
pr_debug("Offset value = %d\n", offset);
if (offset >= size) {
if (size && offset >= size) {
pr_err("%s: Invalid dsp buf offset\n", __func__);
ret = -EFAULT;
q6asm_cpu_buf_release(OUT, prtd->audio_client);
@ -2681,10 +2681,17 @@ static int msm_pcm_channel_mixer_cfg_ctl_put(struct snd_kcontrol *kcontrol,
if (chmixer_pspd->enable && prtd && prtd->audio_client) {
stream_id = prtd->audio_client->session;
be_id = chmixer_pspd->port_idx;
#ifdef CONFIG_PLATFORM_AUTO
msm_pcm_routing_set_channel_mixer_runtime(fe_id, be_id,
stream_id,
session_type,
chmixer_pspd);
#else
msm_pcm_routing_set_channel_mixer_runtime(be_id,
stream_id,
session_type,
chmixer_pspd);
#endif
}
if (reset_override_out_ch_map)

View file

@ -2361,6 +2361,7 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
* msm_pcm_routing_set_channel_mixer_runtime - apply channel mixer
* setting during runtime.
*
* @fe_id: frontend index
* @be_id: backend index
* @session_id: session index
* @session_type: session type
@ -2368,12 +2369,13 @@ static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode,
*
* Retuen: 0 for success, else error
*/
int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
int msm_pcm_routing_set_channel_mixer_runtime(int fe_id, int be_id, int session_id,
int session_type,
struct msm_pcm_channel_mixer *params)
{
int rc = 0;
int port_id, copp_idx = 0;
bool tmp = false;
be_id--;
if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) {
@ -2382,8 +2384,13 @@ int msm_pcm_routing_set_channel_mixer_runtime(int be_id, int session_id,
return -EINVAL;
}
port_id = msm_bedais[be_id].port_id;
copp_idx = adm_get_default_copp_idx(port_id);
tmp = msm_pcm_routing_get_portid_copp_idx(fe_id, session_type, &port_id, &copp_idx);
if(!tmp){
pr_err("%s: Could not find copp_idx for fe_id: %d, will use default copp_idx\n",
__func__, fe_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);

View file

@ -136,6 +136,10 @@ static int afe_loopback_tx_port_id = -1;
static struct msm_pcm_channel_mixer ec_ref_chmix_cfg[MSM_FRONTEND_DAI_MAX];
static struct msm_ec_ref_port_cfg ec_ref_port_cfg;
static int32_t mclk_cfg_be_idx;
static int32_t mclk_cfg_src_id;
static uint32_t mclk_cfg_freq;
#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];
@ -30766,6 +30770,10 @@ static const struct snd_kcontrol_new wsa_cdc_dma_rx_0_port_mixer_controls[] = {
MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0,
MSM_BACKEND_DAI_TX_CDC_DMA_TX_3, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
SOC_DOUBLE_EXT("SLIM_7_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0,
MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
SOC_DOUBLE_EXT("SLIM_8_TX", SND_SOC_NOPM,
MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0,
MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
@ -36837,6 +36845,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"},
{"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"},
{"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"},
{"SLIM7_UL_HL", NULL, "SLIMBUS_7_TX"},
{"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"},
{"WSA_CDC_DMA_RX_0_DL_HL", "Switch", "CDC_DMA_DL_HL"},
{"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0_DL_HL"},
@ -36962,6 +36971,7 @@ static const struct snd_soc_dapm_route intercon[] = {
/* connect to INT4_MI2S_DL_HL since same pcm_id */
{"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"},
{"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"WSA_CDC_DMA_RX_0 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"WSA_CDC_DMA_RX_0", NULL, "WSA_CDC_DMA_RX_0 Port Mixer"},
@ -42813,6 +42823,16 @@ static const struct snd_kcontrol_new
},
};
static int msm_routing_get_mclk_src_cfg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = mclk_cfg_be_idx;
ucontrol->value.integer.value[1] = mclk_cfg_src_id;
ucontrol->value.integer.value[2] = mclk_cfg_freq;
return 0;
}
static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -42825,6 +42845,10 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
mclk_src_id = ucontrol->value.integer.value[1];
mclk_freq = ucontrol->value.integer.value[2];
mclk_cfg_be_idx = be_idx;
mclk_cfg_src_id = mclk_src_id;
mclk_cfg_freq = mclk_freq;
if (be_idx < 0 || be_idx >= MSM_BACKEND_DAI_MAX) {
pr_err("%s: Invalid be id %d\n", __func__, be_idx);
return -EINVAL;
@ -42854,7 +42878,8 @@ static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new mclk_src_controls[] = {
SOC_SINGLE_MULTI_EXT("MCLK_SRC CFG", SND_SOC_NOPM, 0, 24576000, 0, 3,
NULL, msm_routing_put_mclk_src_cfg),
msm_routing_get_mclk_src_cfg,
msm_routing_put_mclk_src_cfg),
};
static int msm_routing_stereo_channel_reverse_control_get(

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/*
* Add support for 24 and 32bit format for ASM loopback and playback session.
@ -818,10 +818,17 @@ int msm_pcm_routing_set_channel_mixer_cfg(
int fe_id, int session_type,
struct msm_pcm_channel_mixer *params);
#ifdef CONFIG_PLATFORM_AUTO
int msm_pcm_routing_set_channel_mixer_runtime(
int be_id, int session_id,
int fe_id, int be_id, int session_id,
int session_type,
struct msm_pcm_channel_mixer *params);
#else
int msm_pcm_routing_set_channel_mixer_runtime(
int be_id, int session_id,
int session_type,
struct msm_pcm_channel_mixer *params);
#endif
int msm_pcm_routing_set_stream_ec_ref_chmix_cfg(
int fedai_id, struct msm_pcm_channel_mixer *cfg_data);

View file

@ -1713,16 +1713,11 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv)
if (data->opcode == APR_BASIC_RSP_RESULT) {
pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n",
__func__, payload[0]);
if (!((client_id != ADM_CLIENT_ID_SOURCE_TRACKING) &&
((payload[0] == ADM_CMD_SET_PP_PARAMS_V5) ||
(payload[0] == ADM_CMD_SET_PP_PARAMS_V6)))) {
if (data->payload_size <
(2 * sizeof(uint32_t))) {
pr_err("%s: Invalid payload size %d\n",
__func__, data->payload_size);
return 0;
}
if (data->payload_size <
(2 * sizeof(uint32_t))) {
pr_err("%s: Invalid payload size %d\n",
__func__, data->payload_size);
return 0;
}
if (payload[1] != 0) {

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/slab.h>
#include <linux/debugfs.h>
@ -785,8 +785,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
switch (param_hdr.param_id) {
case AFE_PARAM_ID_CALIB_RES_CFG_V2:
num_ch = data_start[0];
expected_size += sizeof(struct asm_calib_res_cfg);
if (param_hdr.param_size != sizeof(struct asm_calib_res_cfg)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (sizeof(struct asm_calib_res_cfg) - ((2 - num_ch) * 4))) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -794,8 +800,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
data_dest = (u32 *) &this_afe.calib_data;
break;
case AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS:
num_ch = data_start[0];
expected_size += sizeof(struct afe_sp_th_vi_ftm_params);
if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_ftm_params)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (sizeof(struct afe_sp_th_vi_ftm_params) - ((2 - num_ch) * 4 * 3))) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -803,8 +815,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
data_dest = (u32 *) &this_afe.th_vi_resp;
break;
case AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_PARAMS:
num_ch = data_start[0];
expected_size += sizeof(struct afe_sp_th_vi_v_vali_params);
if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_v_vali_params)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (sizeof(struct afe_sp_th_vi_v_vali_params) - ((2 - num_ch) * 4 * 2))) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -812,8 +830,14 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
data_dest = (u32 *) &this_afe.th_vi_v_vali_resp;
break;
case AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS:
num_ch = data_start[0];
expected_size += sizeof(struct afe_sp_ex_vi_ftm_params);
if (param_hdr.param_size != sizeof(struct afe_sp_ex_vi_ftm_params)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (sizeof(struct afe_sp_ex_vi_ftm_params) - ((2 - num_ch) * 4 * 4))) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -821,9 +845,15 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
data_dest = (u32 *) &this_afe.ex_vi_resp;
break;
case AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING:
num_ch = data_start[0];
expected_size += sizeof(
struct afe_sp_rx_tmax_xmax_logging_param);
if (param_hdr.param_size != sizeof(struct afe_sp_rx_tmax_xmax_logging_param)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (num_ch * sizeof(struct afe_sp_rx_tmax_xmax_logging_param))) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -831,10 +861,16 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload,
data_dest = (u32 *) &this_afe.xt_logging_resp;
break;
case AFE_PARAM_ID_SP_V4_CALIB_RES_CFG:
num_ch = data_start[0];
expected_size += sizeof(
struct afe_sp_v4_param_th_vi_calib_res_cfg);
if (param_hdr.param_size != sizeof(
struct afe_sp_v4_param_th_vi_calib_res_cfg)) {
if (num_ch > SP_V2_NUM_MAX_SPKRS) {
pr_err("%s: Error: num_ch %d is greater than expected\n",
__func__,num_ch);
return -EINVAL;
}
if (param_hdr.param_size != (sizeof(
struct afe_sp_v4_param_th_vi_calib_res_cfg) - (2 - num_ch) * 4)) {
pr_err("%s: Error: param_size %d is greater than expected\n",
__func__,param_hdr.param_size);
return -EINVAL;
@ -9858,8 +9894,8 @@ int afe_set_pll_clk_drift(u16 port_id, int32_t set_clk_drift,
}
EXPORT_SYMBOL(afe_set_pll_clk_drift);
static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
uint32_t mclk_freq)
int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
uint32_t mclk_freq)
{
struct param_hdr_v3 param_hdr;
struct afe_param_id_clock_set_v2_t dyn_mclk_cfg;
@ -9871,6 +9907,12 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
return ret;
}
if (!afe_ext_mclk.ext_mclk_cb) {
pr_debug("%s: ext_mclk_cb not registered; platform doesn't support ext clk\n",
__func__);
return -EOPNOTSUPP;
}
if (index < 0 || index >= AFE_MAX_PORTS) {
pr_err("%s: index[%d] invalid!\n", __func__, index);
return -EINVAL;
@ -9883,34 +9925,33 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
param_hdr.param_size = sizeof(struct afe_param_id_clock_set_v2_t);
memset(&dyn_mclk_cfg, 0, sizeof(dyn_mclk_cfg));
dyn_mclk_cfg.clk_freq_in_hz = cfg->clk_freq_in_hz;
if (afe_ext_mclk.ext_mclk_cb) {
ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
cfg->enable, mclk_freq, &dyn_mclk_cfg);
if (ret) {
pr_err_ratelimited("%s: get mclk cfg failed %d\n",
__func__, ret);
return ret;
}
} else {
pr_err_ratelimited("%s: mclk callback not registered\n",
__func__);
return -EINVAL;
}
dyn_mclk_cfg.clk_freq_in_hz = cfg->clk_freq_in_hz;
dyn_mclk_cfg.clk_set_minor_version = 1;
dyn_mclk_cfg.clk_id = cfg->clk_id;
dyn_mclk_cfg.clk_attri = cfg->clk_attri;
dyn_mclk_cfg.enable = cfg->enable;
pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__,
dyn_mclk_cfg.clk_set_minor_version, dyn_mclk_cfg.clk_id);
dyn_mclk_cfg.clk_set_minor_version, dyn_mclk_cfg.clk_id);
pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__,
dyn_mclk_cfg.clk_freq_in_hz, dyn_mclk_cfg.clk_attri);
pr_debug("%s: clk root = 0x%x clk enable = 0x%x\n", __func__,
dyn_mclk_cfg.clk_root, dyn_mclk_cfg.enable);
pr_debug("%s: divider_2x =%d m = %d n = %d, d =%d\n", __func__,
dyn_mclk_cfg.divider_2x, dyn_mclk_cfg.m, dyn_mclk_cfg.n,
dyn_mclk_cfg.d);
dyn_mclk_cfg.clk_freq_in_hz, dyn_mclk_cfg.clk_attri);
pr_debug("%s: clk input freq (Hz) = %d clk enable = 0x%x\n", __func__,
mclk_freq, dyn_mclk_cfg.enable);
if (dyn_mclk_cfg.enable) {
/* callback to populate Div2x, M, N, D, clock root and enable GPIOs */
ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
dyn_mclk_cfg.enable, mclk_freq, &dyn_mclk_cfg);
if (ret) {
pr_err_ratelimited("%s: mclk cb during enable failed %d\n",
__func__, ret);
return ret;
}
pr_debug("%s: clk root = 0x%x divider_2x = %d m = %d n = %d d = %d\n",
__func__, dyn_mclk_cfg.clk_root, dyn_mclk_cfg.divider_2x,
dyn_mclk_cfg.m, dyn_mclk_cfg.n, dyn_mclk_cfg.d);
}
ret = afe_q6_interface_prepare();
if (ret != 0) {
@ -9922,23 +9963,108 @@ static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
mutex_lock(&this_afe.afe_cmd_lock);
ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr,
(u8 *) &dyn_mclk_cfg);
if (ret < 0)
if (ret < 0) {
pr_err_ratelimited("%s: ext MCLK clk cfg failed with ret %d\n",
__func__, ret);
__func__, ret);
mutex_unlock(&this_afe.afe_cmd_lock);
goto stop_mclk;
}
mutex_unlock(&this_afe.afe_cmd_lock);
if (!dyn_mclk_cfg.enable) {
/* callback to disable GPIOs */
ret = afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
dyn_mclk_cfg.enable, mclk_freq, &dyn_mclk_cfg);
if (ret) {
pr_err_ratelimited("%s: mclk cb during disable failed %d\n",
__func__, ret);
return ret;
}
}
if (ret >= 0)
return ret;
stop_mclk:
if (afe_ext_mclk.ext_mclk_cb && cfg->enable) {
afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
cfg->enable, mclk_freq, &dyn_mclk_cfg);
}
dyn_mclk_cfg.enable = 0;
afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
dyn_mclk_cfg.enable, mclk_freq, &dyn_mclk_cfg);
return ret;
}
EXPORT_SYMBOL(afe_set_lpass_clk_cfg_ext_mclk);
int afe_set_lpass_ext_mclk_mux_cfg(const char *mux_str,
uint32_t mux_val)
{
struct param_hdr_v3 param_hdr;
struct afe_param_id_clock_mux_cfg_t clk_mux_cfg;
uint32_t build_major_version = 0;
uint32_t build_minor_version = 0;
uint32_t build_branch_version = 0;
int afe_api_version = 0;
int ret = 0;
if (!mux_str) {
pr_err("%s: mux_str is NULL\n", __func__);
ret = -EINVAL;
return ret;
}
ret = q6core_get_avcs_avs_build_version_info(
&build_major_version, &build_minor_version,
&build_branch_version);
if (ret < 0)
return ret;
ret = q6core_get_avcs_api_version_per_service(
APRV2_IDS_SERVICE_ID_ADSP_AFE_V);
if (ret < 0)
return ret;
afe_api_version = ret;
pr_debug("%s: mjor: %u, mnor: %u, brnch: %u, afe_api: %u\n",
__func__, build_major_version, build_minor_version,
build_branch_version, afe_api_version);
if ((build_major_version != AVS_BUILD_MAJOR_VERSION_V2) ||
(build_minor_version != AVS_BUILD_MINOR_VERSION_V9) ||
(build_branch_version != AVS_BUILD_BRANCH_VERSION_V0) ||
(afe_api_version < AFE_API_VERSION_V11)) {
pr_err("%s: AFE_PARAM_ID_CLOCK_MUX_CFG unsupported by AVS\n", __func__);
return -EINVAL;
}
memset(&param_hdr, 0, sizeof(param_hdr));
param_hdr.module_id = AFE_MODULE_CLOCK_SET;
param_hdr.instance_id = INSTANCE_ID_0;
param_hdr.param_id = AFE_PARAM_ID_CLOCK_MUX_CFG;
param_hdr.param_size = sizeof(struct afe_param_id_clock_mux_cfg_t);
memset(&clk_mux_cfg, 0, sizeof(clk_mux_cfg));
strlcpy(clk_mux_cfg.mux_string, mux_str, sizeof(clk_mux_cfg.mux_string));
clk_mux_cfg.mux_value = mux_val;
pr_debug("%s: ext mclk mux cfg - mux_string: %s, mux_value = %u\n", __func__,
clk_mux_cfg.mux_string, clk_mux_cfg.mux_value);
ret = afe_q6_interface_prepare();
if (ret != 0) {
pr_err_ratelimited("%s: Q6 interface prepare failed %d\n",
__func__, ret);
return ret;
}
mutex_lock(&this_afe.afe_cmd_lock);
ret = q6afe_svc_pack_and_set_param_in_band(IDX_RSVD_3, param_hdr,
(u8 *) &clk_mux_cfg);
if (ret < 0) {
pr_err_ratelimited("%s: ext mclk mux cfg failed with ret %d\n",
__func__, ret);
}
mutex_unlock(&this_afe.afe_cmd_lock);
return ret;
}
EXPORT_SYMBOL(afe_set_lpass_ext_mclk_mux_cfg);
static int ext_dyn_mclk_port_id;
static int ext_dyn_clk_root = Q6AFE_LPASS_CLK_ROOT_DEFAULT;
@ -10162,7 +10288,8 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
build_branch_version, afe_api_version);
if ((build_major_version != AVS_BUILD_MAJOR_VERSION_V2) ||
(build_minor_version != AVS_BUILD_MINOR_VERSION_V9) ||
(build_branch_version != AVS_BUILD_BRANCH_VERSION_V3) ||
((build_branch_version != AVS_BUILD_BRANCH_VERSION_V0) &&
(build_branch_version != AVS_BUILD_BRANCH_VERSION_V3)) ||
(afe_api_version < AFE_API_VERSION_V8)) {
pr_err("%s: ext mclk not supported by AVS\n", __func__);
return -EINVAL;

View file

@ -8843,6 +8843,8 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
}
mmap_regions = (struct avs_cmd_shared_mem_map_regions *)
mmap_region_cmd;
mutex_lock(&ac->cmd_lock);
q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir);
atomic_set(&ac->mem_state, -1);
pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__,
@ -8900,7 +8902,6 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
buffer_node = NULL;
goto fail_cmd;
}
mutex_lock(&ac->cmd_lock);
for (i = 0; i < bufcnt; i++) {
ab = &port->buf[i];
@ -8913,9 +8914,9 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
buffer_node[i].mmap_hdl);
}
ac->port[dir].tmp_hdl = 0;
mutex_unlock(&ac->cmd_lock);
rc = 0;
fail_cmd:
mutex_unlock(&ac->cmd_lock);
kfree(mmap_region_cmd);
mmap_region_cmd = NULL;
return rc;

View file

@ -1506,8 +1506,10 @@ int q6core_map_mdf_memory_regions(uint64_t *buf_add, uint32_t mempool_id,
* bufcnt;
mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
if (mmap_region_cmd == NULL)
if (mmap_region_cmd == NULL) {
mutex_unlock(&q6core_lcl.cmd_lock);
return -ENOMEM;
}
mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2021, Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/fs.h>
#include <linux/mutex.h>
@ -2130,6 +2130,12 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv)
return 0;
}
if (data->payload_size < (2 * sizeof(uint32_t))) {
pr_err("%s: payload has invalid size[%d]\n", __func__,
data->payload_size);
return -EINVAL;
}
command = payload[0];
retcode = payload[1];
sid = (data->token >> 8) & 0x0F;
@ -2454,6 +2460,12 @@ int q6lsm_set_one_param(struct lsm_client *client,
sizeof(struct param_hdr_v2);
if (param_type == LSM_REG_MULTI_SND_MODEL) {
if(list_empty(&client->stage_cfg[p_info->stage_idx].sound_models)) {
pr_err("%s: sound_models list is empty \n",
__func__);
return -EINVAL;
}
list_for_each_entry(sm,
&client->stage_cfg[p_info->stage_idx].sound_models,
list) {

View file

@ -6898,6 +6898,7 @@ int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
struct media_format_info *finfo)
{
struct voice_data *v = voice_get_session(session_id);
int ret = 0;
if (v == NULL) {
pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
@ -6929,12 +6930,12 @@ int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
break;
default:
pr_err("%s: Invalid path_dir %d\n", __func__, path_dir);
return -EINVAL;
ret = -EINVAL;
}
mutex_unlock(&v->lock);
return 0;
return ret;
}
EXPORT_SYMBOL(voc_set_device_config);

View file

@ -12549,6 +12549,17 @@ struct afe_param_id_clock_set_v2_t {
uint32_t d;
};
#define AFE_PARAM_ID_CLOCK_MUX_CFG 0x000102fd
struct afe_param_id_clock_mux_cfg_t {
char mux_string[128];
/* Name of the Mux string.
* String name may varies for each target, so HLOS must pass the proper string based on IPCAT.
* @values Valid string with a maximum of 128 characters
*/
uint32_t mux_value;
/* Value of the external m-clock. */
};
struct afe_clk_cfg {
/* Minor version used for tracking the version of the I2S
* configuration interface.

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __Q6AFE_V2_H__
#define __Q6AFE_V2_H__
@ -55,6 +55,8 @@
/* for external mclk dynamic switch */
#define AFE_API_VERSION_V8 8
#define AFE_API_VERSION_V10 10
/* for external mclk selection through mux */
#define AFE_API_VERSION_V11 11
#define AFE_SAMPLING_RATE_8KHZ 8000
@ -419,6 +421,7 @@ enum afe_mclk_freq {
MCLK_FREQ_11P2896_MHZ = MCLK_FREQ_MIN,
MCLK_FREQ_12P288_MHZ,
MCLK_FREQ_16P384_MHZ,
MCLK_FREQ_19P200_MHZ,
MCLK_FREQ_22P5792_MHZ,
MCLK_FREQ_24P576_MHZ,
MCLK_FREQ_MAX,
@ -603,8 +606,11 @@ int afe_port_send_afe_limiter_param(u16 port_id,
struct afe_param_id_port_afe_limiter_disable_t *disable_limiter);
int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats,
u16 port);
int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
uint32_t mclk_freq);
int afe_set_lpass_clk_cfg_ext_mclk_v2(int index,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg, uint32_t mclk_freq);
int afe_set_lpass_ext_mclk_mux_cfg(const char *mux_str, uint32_t mux_val);
int afe_get_sp_rx_tmax_xmax_logging_data(
struct afe_sp_rx_tmax_xmax_logging_param *xt_logging,
u16 port_id);
@ -634,6 +640,14 @@ int afe_set_source_clk(u16 port_id, const char *clk_src);
void afe_set_clk_src_array(const char *clk_src[CLK_SRC_MAX]);
int afe_set_mclk_src_cfg(u16 port_id, uint32_t mclk_src_id, uint32_t mclk_freq);
/* Client of AFE registers afe_enable_mclk_and_get_info_cb_func callback function via
* afe_register_ext_mclk_cb() when external clock is supported by the platform. During
* clock enable sequence, AFE triggers this callback before requesting DSP for clock
* enable. Client can provide Div2X, M, N, D and clock root as part of this callback.
* Client can also enable required GPIO that are essential to route external clock in
* this callback. During clock disable sequence, AFE triggers this callback after
* requesting DSP for clock disable. Client can disable required GPIOs in this callback.
*/
typedef int (*afe_enable_mclk_and_get_info_cb_func) (void *private_data,
uint32_t enable, uint32_t mclk_freq,
struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg);

View file

@ -18,6 +18,12 @@
#define SWR_CLK_RATE_4P8MHZ 4800000
#define SWR_CLK_RATE_9P6MHZ 9600000
#define SWR_CLK_RATE_11P2896MHZ 11289600
#define SWR_CLK_RATE_0P384MHZ 384000
#define SWR_CLK_RATE_0P768MHZ 768000
#define SWR_CLK_RATE_1P536MHZ 1536000
#define SWR_CLK_RATE_3P072MHZ 3072000
#define SWR_CLK_RATE_6P144MHZ 6144000
#define SWR_CLK_RATE_12P288MHZ 12288000
extern struct bus_type soundwire_type;
struct swr_device;

View file

@ -34,6 +34,7 @@ struct swr_mstr_port {
#define MCLK_FREQ 9600000
#define MCLK_FREQ_LP 600000
#define MCLK_FREQ_NATIVE 11289600
#define MCLK_FREQ_12P288MHZ 12288000
#if (IS_ENABLED(CONFIG_SOUNDWIRE_WCD_CTRL) || \
IS_ENABLED(CONFIG_SOUNDWIRE_MSTR_CTRL))

View file

@ -26,9 +26,6 @@
#include "core.h"
#include "pinctrl-utils.h"
#define LPI_AUTO_SUSPEND_DELAY 100 /* delay in msec */
#define LPI_AUTO_SUSPEND_DELAY_ERROR 1 /* delay in msec */
#define LPI_ADDRESS_SIZE 0x20000
#define LPI_SLEW_ADDRESS_SIZE 0x1000
@ -118,8 +115,6 @@ struct lpi_gpio_state {
struct clk *lpass_core_hw_vote;
struct clk *lpass_audio_hw_vote;
struct mutex slew_access_lock;
bool core_hw_vote_status;
struct mutex core_hw_vote_lock;
};
static const char *const lpi_gpio_groups[] = {
@ -144,12 +139,54 @@ static const char *const lpi_gpio_functions[] = {
[LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5,
};
int lpi_pinctrl_runtime_suspend(struct device *dev);
int lpi_pinctrl_hw_vote(struct device *dev)
{
int ret = 0;
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return -EINVAL;
}
hw_vote = state->lpass_audio_hw_vote;
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_vote\n", __func__);
ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote);
if (ret < 0) {
dev_err(dev, "%s:lpass core hw island enable failed\n",
__func__);
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_vote done\n", __func__);
return ret;
}
int lpi_pinctrl_hw_unvote(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return -EINVAL;
}
hw_vote = state->lpass_audio_hw_vote;
}
dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote\n", __func__);
digital_cdc_rsc_mgr_hw_vote_disable(hw_vote);
dev_dbg(dev, "%s: lpi_pinctrl_hw_unvote done\n", __func__);
return 0;
}
static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
{
int ret = 0;
struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev);
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if (!lpi_dev_up) {
@ -158,52 +195,38 @@ static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr)
__func__);
return 0;
}
pm_runtime_get_sync(lpi_dev);
mutex_lock(&state->core_hw_vote_lock);
if (!state->core_hw_vote_status) {
if (__ratelimit(&rtl))
pr_err("%s: core hw vote clk is not enabled\n",
__func__);
ret = -EINVAL;
ret = lpi_pinctrl_hw_vote(lpi_dev);
if (ret < 0)
goto err;
}
ret = ioread32(pad->base + pad->offset + addr);
if (ret < 0)
pr_err("%s: read 0x%x failed\n", __func__, addr);
lpi_pinctrl_hw_unvote(lpi_dev);
err:
mutex_unlock(&state->core_hw_vote_lock);
pm_runtime_mark_last_busy(lpi_dev);
pm_runtime_put_autosuspend(lpi_dev);
return ret;
}
static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr,
unsigned int val)
{
struct lpi_gpio_state *state = dev_get_drvdata(lpi_dev);
int ret = 0;
static DEFINE_RATELIMIT_STATE(rtl, 1 * HZ, 1);
if (!lpi_dev_up) {
return 0;
}
pm_runtime_get_sync(lpi_dev);
mutex_lock(&state->core_hw_vote_lock);
if (!state->core_hw_vote_status) {
if (__ratelimit(&rtl))
pr_err("%s: core hw vote clk is not enabled\n",
__func__);
ret = -EINVAL;
ret = lpi_pinctrl_hw_vote(lpi_dev);
if (ret < 0)
goto err;
}
iowrite32(val, pad->base + pad->offset + addr);
lpi_pinctrl_hw_unvote(lpi_dev);
err:
mutex_unlock(&state->core_hw_vote_lock);
pm_runtime_mark_last_busy(lpi_dev);
pm_runtime_put_autosuspend(lpi_dev);
return ret;
}
@ -516,39 +539,6 @@ static int lpi_notifier_service_cb(struct notifier_block *this,
return NOTIFY_OK;
}
int lpi_pinctrl_suspend(struct device *dev)
{
int ret = 0;
dev_dbg(dev, "%s: system suspend\n", __func__);
if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) {
ret = lpi_pinctrl_runtime_suspend(dev);
if (!ret) {
/*
* Synchronize runtime-pm and system-pm states:
* At this point, we are already suspended. If
* runtime-pm still thinks its active, then
* make sure its status is in sync with HW
* status. The three below calls let the
* runtime-pm know that we are suspended
* already without re-invoking the suspend
* callback
*/
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
}
}
return ret;
}
int lpi_pinctrl_resume(struct device *dev)
{
return 0;
}
static struct notifier_block service_nb = {
.notifier_call = lpi_notifier_service_cb,
.priority = -INT_MAX,
@ -557,7 +547,6 @@ static struct notifier_block service_nb = {
static void lpi_pinctrl_ssr_disable(struct device *dev, void *data)
{
lpi_dev_up = false;
lpi_pinctrl_suspend(dev);
}
static const struct snd_event_ops lpi_pinctrl_ssr_ops = {
@ -773,7 +762,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
state->chip.can_sleep = false;
mutex_init(&state->slew_access_lock);
mutex_init(&state->core_hw_vote_lock);
state->ctrl = devm_pinctrl_register(dev, pctrldesc, state);
if (IS_ERR(state->ctrl))
@ -832,12 +820,6 @@ static int lpi_pinctrl_probe(struct platform_device *pdev)
}
state->lpass_audio_hw_vote = lpass_audio_hw_vote;
state->core_hw_vote_status = false;
pm_runtime_set_autosuspend_delay(&pdev->dev, LPI_AUTO_SUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
err_snd_evt:
@ -845,7 +827,6 @@ err_snd_evt:
err_range:
gpiochip_remove(&state->chip);
err_chip:
mutex_destroy(&state->core_hw_vote_lock);
mutex_destroy(&state->slew_access_lock);
err_io:
return ret;
@ -855,13 +836,9 @@ 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->core_hw_vote_lock);
mutex_destroy(&state->slew_access_lock);
return 0;
@ -874,79 +851,9 @@ static const struct of_device_id lpi_pinctrl_of_match[] = {
MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match);
int lpi_pinctrl_runtime_resume(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
int ret = 0;
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return 0;
}
hw_vote = state->lpass_audio_hw_vote;
}
mutex_lock(&state->core_hw_vote_lock);
ret = digital_cdc_rsc_mgr_hw_vote_enable(hw_vote);
if (ret < 0) {
pm_runtime_set_autosuspend_delay(dev,
LPI_AUTO_SUSPEND_DELAY_ERROR);
dev_err(dev, "%s:lpass core hw island enable failed\n",
__func__);
goto exit;
} else {
state->core_hw_vote_status = true;
}
pm_runtime_set_autosuspend_delay(dev, LPI_AUTO_SUSPEND_DELAY);
exit:
mutex_unlock(&state->core_hw_vote_lock);
return 0;
}
int lpi_pinctrl_runtime_suspend(struct device *dev)
{
struct lpi_gpio_state *state = dev_get_drvdata(dev);
struct clk *hw_vote = state->lpass_core_hw_vote;
if (state->lpass_core_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid core hw node\n", __func__);
if (state->lpass_audio_hw_vote == NULL) {
dev_dbg(dev, "%s: Invalid audio hw node\n", __func__);
return 0;
}
hw_vote = state->lpass_audio_hw_vote;
}
mutex_lock(&state->core_hw_vote_lock);
if (state->core_hw_vote_status) {
digital_cdc_rsc_mgr_hw_vote_disable(hw_vote);
state->core_hw_vote_status = false;
}
mutex_unlock(&state->core_hw_vote_lock);
return 0;
}
static const struct dev_pm_ops lpi_pinctrl_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(
lpi_pinctrl_suspend,
lpi_pinctrl_resume
)
SET_RUNTIME_PM_OPS(
lpi_pinctrl_runtime_suspend,
lpi_pinctrl_runtime_resume,
NULL
)
};
static struct platform_driver lpi_pinctrl_driver = {
.driver = {
.name = "qcom-lpi-pinctrl",
.pm = &lpi_pinctrl_dev_pm_ops,
.of_match_table = lpi_pinctrl_of_match,
.suppress_bind_attrs = true,
},

View file

@ -1160,8 +1160,8 @@ static void swrm_switch_frame_shape(struct swr_mstr_ctrl *swrm, int mclk_freq)
} else {
n_col = SWR_MIN_COL;
col = SWRM_COL_02;
n_row = SWR_ROW_50;
row = SWRM_ROW_50;
n_row = (mclk_freq == MCLK_FREQ_12P288MHZ) ? SWR_ROW_64 : SWR_ROW_50;
row = (mclk_freq == MCLK_FREQ_12P288MHZ) ? SWRM_ROW_64 : SWRM_ROW_50;
frame_sync = SWRM_FRAME_SYNC_SEL;
}
@ -1222,16 +1222,25 @@ int swrm_get_clk_div_rate(int mclk_freq, int bus_clk_freq)
if (mclk_freq == SWR_CLK_RATE_9P6MHZ) {
if (bus_clk_freq <= SWR_CLK_RATE_0P6MHZ)
bus_clk_freq = SWR_CLK_RATE_0P6MHZ;
else if (bus_clk_freq <= SWR_CLK_RATE_1P2MHZ)
bus_clk_freq = SWR_CLK_RATE_4P8MHZ;
else if (bus_clk_freq <= SWR_CLK_RATE_2P4MHZ)
bus_clk_freq = SWR_CLK_RATE_4P8MHZ;
else if(bus_clk_freq <= SWR_CLK_RATE_4P8MHZ)
else if ((bus_clk_freq <= SWR_CLK_RATE_1P2MHZ) ||
(bus_clk_freq <= SWR_CLK_RATE_2P4MHZ) ||
(bus_clk_freq <= SWR_CLK_RATE_4P8MHZ))
bus_clk_freq = SWR_CLK_RATE_4P8MHZ;
else if(bus_clk_freq <= SWR_CLK_RATE_9P6MHZ)
bus_clk_freq = SWR_CLK_RATE_9P6MHZ;
else
bus_clk_freq = SWR_CLK_RATE_9P6MHZ;
} else if (mclk_freq == SWR_CLK_RATE_12P288MHZ) {
if (bus_clk_freq <= SWR_CLK_RATE_0P768MHZ)
bus_clk_freq = SWR_CLK_RATE_0P768MHZ;
else if ((bus_clk_freq <= SWR_CLK_RATE_1P536MHZ) ||
(bus_clk_freq <= SWR_CLK_RATE_3P072MHZ) ||
(bus_clk_freq <= SWR_CLK_RATE_6P144MHZ))
bus_clk_freq = SWR_CLK_RATE_6P144MHZ;
else if(bus_clk_freq <= SWR_CLK_RATE_12P288MHZ)
bus_clk_freq = SWR_CLK_RATE_12P288MHZ;
else
bus_clk_freq = SWR_CLK_RATE_12P288MHZ;
} else if (mclk_freq == SWR_CLK_RATE_11P2896MHZ)
bus_clk_freq = SWR_CLK_RATE_11P2896MHZ;
@ -1368,12 +1377,14 @@ static void swrm_get_device_frame_shape(struct swr_mstr_ctrl *swrm,
if ((swrm->master_id == MASTER_ID_TX) &&
(swrm->use_custom_phy_addr) &&
((swrm->bus_clk == SWR_CLK_RATE_9P6MHZ) ||
((swrm->bus_clk == SWR_CLK_RATE_12P288MHZ) ||
(swrm->bus_clk == SWR_CLK_RATE_9P6MHZ) ||
(swrm->bus_clk == SWR_CLK_RATE_0P6MHZ) ||
(swrm->bus_clk == SWR_CLK_RATE_4P8MHZ))) {
dev_num = swrm_get_device_id(swrm, port_req->dev_num);
port_id = port_req->slave_port_id;
if (swrm->bus_clk == SWR_CLK_RATE_9P6MHZ)
if ((swrm->bus_clk == SWR_CLK_RATE_12P288MHZ) ||
(swrm->bus_clk == SWR_CLK_RATE_9P6MHZ))
pp_dev = swrdev_frame_params_9p6MHz[dev_num].pp;
else if (swrm->bus_clk == SWR_CLK_RATE_0P6MHZ)
pp_dev = swrdev_frame_params_0p6MHz[dev_num].pp;
@ -1681,7 +1692,8 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable)
/* set col = 16 */
n_col = SWR_MAX_COL;
col = SWRM_COL_16;
if (swrm->bus_clk == MCLK_FREQ_LP) {
if ((swrm->bus_clk == MCLK_FREQ_LP) ||
(swrm->bus_clk == SWR_CLK_RATE_0P768MHZ)) {
n_col = SWR_MIN_COL;
col = SWRM_COL_02;
}
@ -1699,16 +1711,15 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable)
}
/* Use default 50 * x, frame shape. Change based on mclk */
if (swrm->mclk_freq == MCLK_FREQ_NATIVE) {
dev_dbg(swrm->dev, "setting 64 x %d frameshape\n", col);
n_row = SWR_ROW_64;
row = SWRM_ROW_64;
frame_sync = SWRM_FRAME_SYNC_SEL_NATIVE;
} else {
dev_dbg(swrm->dev, "setting 50 x %d frameshape\n", col);
n_row = SWR_ROW_50;
row = SWRM_ROW_50;
n_row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? SWR_ROW_64 : SWR_ROW_50;
row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ? SWRM_ROW_64 : SWRM_ROW_50;
frame_sync = SWRM_FRAME_SYNC_SEL;
}
dev_dbg(swrm->dev, "setting %d x %d frameshape\n", row, col);
ssp_period = swrm_get_ssp_period(swrm, row, col, frame_sync);
bus_clk_div_factor = swrm_get_clk_div(swrm->mclk_freq, swrm->bus_clk);
dev_dbg(swrm->dev, "%s: ssp_period: %d, bus_clk_div:%d \n", __func__,
@ -2209,6 +2220,15 @@ handle_irq:
swrm->clk_stop_wakeup = false;
}
break;
case SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH:
dev_err(swrm->dev,
"%s: SWR Port Channel rate mismatch\n",
__func__);
swrm->intr_mask &=
~SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH;
swr_master_write(swrm,
SWRM_CPU1_INTERRUPT_EN, swrm->intr_mask);
break;
default:
dev_err_ratelimited(swrm->dev,
"%s: SWR unknown interrupt value: %d\n",
@ -2450,14 +2470,28 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm)
{
int ret = 0, i = 0;
u32 val;
u8 row_ctrl = SWR_ROW_50;
u8 col_ctrl = SWR_MIN_COL;
u8 row_ctrl = 0, row = 0;
u8 col_ctrl = SWR_MIN_COL, col = SWRM_COL_02;
u8 ssp_period = 1;
u8 retry_cmd_num = 3;
u32 reg[SWRM_MAX_INIT_REG];
u32 value[SWRM_MAX_INIT_REG];
u32 temp = 0;
int len = 0;
int frame_sync = SWRM_FRAME_SYNC_SEL;
if (swrm->mclk_freq == MCLK_FREQ_NATIVE) {
row_ctrl = SWR_ROW_64;
row = SWRM_ROW_64;
frame_sync = SWRM_FRAME_SYNC_SEL_NATIVE;
} else {
row_ctrl = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ?
SWR_ROW_64 : SWR_ROW_50;
row = (swrm->mclk_freq == MCLK_FREQ_12P288MHZ) ?
SWRM_ROW_64 : SWRM_ROW_50;
frame_sync = SWRM_FRAME_SYNC_SEL;
}
/* Change no of retry counts to 1 for wsa to avoid underflow */
if (swrm->master_id == MASTER_ID_WSA)
@ -2475,8 +2509,7 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm)
__func__, temp);
}
}
ssp_period = swrm_get_ssp_period(swrm, SWRM_ROW_50,
SWRM_COL_02, SWRM_FRAME_SYNC_SEL);
ssp_period = swrm_get_ssp_period(swrm, row, col, frame_sync);
dev_dbg(swrm->dev, "%s: ssp_period: %d\n", __func__, ssp_period);
/* Clear Rows and Cols */
@ -2595,7 +2628,7 @@ static int swrm_probe(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm;
struct swr_ctrl_platform_data *pdata;
u32 i, num_ports, port_num, port_type, ch_mask, swrm_hctl_reg = 0;
u32 i, num_ports, port_num, port_type, ch_mask, swrm_hctl_reg = 0, mclk_freq = 0;
u32 *temp, map_size, map_length, ch_iter = 0, old_port_num = 0;
int ret = 0;
struct clk *lpass_core_hw_vote = NULL;
@ -2830,8 +2863,6 @@ static int swrm_probe(struct platform_device *pdev)
swrm->num_rx_chs = 0;
swrm->clk_ref_count = 0;
swrm->swr_irq_wakeup_capable = 0;
swrm->mclk_freq = MCLK_FREQ;
swrm->bus_clk = MCLK_FREQ;
swrm->dev_up = true;
swrm->state = SWR_MSTR_UP;
swrm->ipc_wakeup = false;
@ -2861,6 +2892,17 @@ static int swrm_probe(struct platform_device *pdev)
for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++)
INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list);
ret = of_property_read_u32(pdev->dev.of_node, "qcom,swrm-mclk-clk-rate",
&mclk_freq);
if (ret) {
swrm->mclk_freq = MCLK_FREQ;
dev_dbg(&pdev->dev, "%s SWRM MCLK not specified, using default: %d,\n",
__func__, swrm->mclk_freq);
} else {
swrm->mclk_freq = mclk_freq;
}
swrm->bus_clk = swrm->mclk_freq;
if (of_property_read_u32(pdev->dev.of_node,
"qcom,disable-div2-clk-switch",
&swrm->disable_div2_clk_switch)) {
@ -3092,6 +3134,12 @@ static int swrm_runtime_resume(struct device *dev)
struct swr_master *mstr = &swrm->master;
struct swr_device *swr_dev;
u32 temp = 0;
u8 row_ctrl = SWR_ROW_50;
u8 col_ctrl = SWR_MIN_COL;
if ((swrm->mclk_freq == MCLK_FREQ_NATIVE) ||
(swrm->mclk_freq == MCLK_FREQ_12P288MHZ))
row_ctrl = SWR_ROW_64;
dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n",
__func__, swrm->state);
@ -3178,7 +3226,7 @@ static int swrm_runtime_resume(struct device *dev)
SWRS_SCP_INT_STATUS_MASK_1);
if (swrm->state == SWR_MSTR_SSR) {
mutex_unlock(&swrm->reslock);
enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL);
enable_bank_switch(swrm, 0, row_ctrl, col_ctrl);
mutex_lock(&swrm->reslock);
}
} else {
@ -3235,6 +3283,13 @@ static int swrm_runtime_suspend(struct device *dev)
int current_state = 0;
struct irq_data *irq_data = NULL;
u8 row_ctrl = SWR_ROW_50;
u8 col_ctrl = SWR_MIN_COL;
if ((swrm->mclk_freq == MCLK_FREQ_NATIVE) ||
(swrm->mclk_freq == MCLK_FREQ_12P288MHZ))
row_ctrl = SWR_ROW_64;
dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n",
__func__, swrm->state);
if (swrm->state == SWR_MSTR_SSR_RESET) {
@ -3268,7 +3323,7 @@ static int swrm_runtime_suspend(struct device *dev)
dev_err(dev, "%s: clk stop mode not supported or SSR entry\n",
__func__);
mutex_unlock(&swrm->reslock);
enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL);
enable_bank_switch(swrm, 0, row_ctrl, col_ctrl);
mutex_lock(&swrm->reslock);
swrm_clk_pause(swrm);
swr_master_write(swrm, SWRM_COMP_CFG, 0x00);

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _SWRM_REGISTERS_H
@ -125,6 +126,7 @@
#endif /* CONFIG_SWRM_VER_1P1 */
#define SWRM_INTERRUPT_STATUS_EXT_CLK_STOP_WAKEUP 0x10000
#define SWRM_INTERRUPT_STATUS_DOUT_RATE_MISMATCH 0x20000
#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000