diff --git a/asoc/Kbuild b/asoc/Kbuild old mode 100755 new mode 100644 index b80f81a35fed..90eb471defb4 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -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 diff --git a/asoc/lahaina.c b/asoc/lahaina.c index 536f1738eb22..c271688302fa 100644 --- a/asoc/lahaina.c +++ b/asoc/lahaina.c @@ -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, @@ -227,33 +218,6 @@ struct ext_mclk_src_info { struct ext_mclk_gpio_info *gpio_info; }; -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; -}; - struct tdm_port { u32 mode; u32 channel; @@ -2096,20 +2060,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; @@ -2125,23 +2110,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; diff --git a/asoc/msm-common.c b/asoc/msm-common.c new file mode 100644 index 000000000000..2fd559323f1b --- /dev/null +++ b/asoc/msm-common.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + diff --git a/asoc/msm-common.h b/asoc/msm-common.h new file mode 100644 index 000000000000..94ec25d27320 --- /dev/null +++ b/asoc/msm-common.h @@ -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 +#include +#include + +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 diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index bde03c63f5a7..5e3a4186b744 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -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); }