|
@@ -24,6 +24,7 @@
|
|
|
#include <dsp/msm_audio_ion.h>
|
|
|
#include <sound/info.h>
|
|
|
#include <dsp/audio_prm.h>
|
|
|
+#include <dsp/digital-cdc-rsc-mgr.h>
|
|
|
|
|
|
#include "msm_common.h"
|
|
|
|
|
@@ -47,6 +48,11 @@ struct snd_card_pdata {
|
|
|
#define TDM_MAX_SLOTS 8
|
|
|
#define MI2S_NUM_CHANNELS 2
|
|
|
|
|
|
+#define SAMPLING_RATE_44P1KHZ 44100
|
|
|
+#define SAMPLING_RATE_88P2KHZ 88200
|
|
|
+#define SAMPLING_RATE_176P4KHZ 176400
|
|
|
+#define SAMPLING_RATE_352P8KHZ 352800
|
|
|
+
|
|
|
static struct attribute device_state_attr = {
|
|
|
.name = "state",
|
|
|
.mode = 0660,
|
|
@@ -244,6 +250,20 @@ static int get_intf_index(const char *stream_name)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+static bool is_fractional_sample_rate(unsigned int sample_rate)
|
|
|
+{
|
|
|
+ switch (sample_rate) {
|
|
|
+ case SAMPLING_RATE_44P1KHZ:
|
|
|
+ case SAMPLING_RATE_88P2KHZ:
|
|
|
+ case SAMPLING_RATE_176P4KHZ:
|
|
|
+ case SAMPLING_RATE_352P8KHZ:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static int get_mi2s_clk_id(int index)
|
|
|
{
|
|
|
int clk_id;
|
|
@@ -306,6 +326,38 @@ static int get_tdm_clk_id(int index)
|
|
|
return clk_id;
|
|
|
}
|
|
|
|
|
|
+int mi2s_tdm_hw_vote_req(struct msm_common_pdata *pdata, int enable)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!pdata || (pdata->lpass_audio_hw_vote == NULL)) {
|
|
|
+ pr_err("%s: pdata or lpass audio hw vote node NULL", __func__);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_debug("%s: lpass audio hw vote for fractional sample rate enable: %d\n",
|
|
|
+ __func__, enable);
|
|
|
+
|
|
|
+ if (enable) {
|
|
|
+ if (atomic_read(&pdata->lpass_audio_hw_vote_ref_cnt) == 0) {
|
|
|
+ ret = digital_cdc_rsc_mgr_hw_vote_enable(pdata->lpass_audio_hw_vote);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("%s lpass audio hw vote enable failed %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ atomic_inc(&pdata->lpass_audio_hw_vote_ref_cnt);
|
|
|
+ } else {
|
|
|
+ atomic_dec(&pdata->lpass_audio_hw_vote_ref_cnt);
|
|
|
+ if (atomic_read(&pdata->lpass_audio_hw_vote_ref_cnt) == 0)
|
|
|
+ digital_cdc_rsc_mgr_hw_vote_disable(pdata->lpass_audio_hw_vote);
|
|
|
+ else if (atomic_read(&pdata->lpass_audio_hw_vote_ref_cnt) < 0)
|
|
|
+ atomic_set(&pdata->lpass_audio_hw_vote_ref_cnt, 0);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
|
|
|
struct snd_pcm_hw_params *params)
|
|
|
{
|
|
@@ -332,7 +384,7 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
|
|
if (index >= 0) {
|
|
|
mutex_lock(&pdata->lock[index]);
|
|
|
- if (pdata->mi2s_gpio_p[index]) {
|
|
|
+ if (atomic_read(&pdata->lpass_intf_clk_ref_cnt[index]) == 0) {
|
|
|
if ((strnstr(stream_name, "TDM", strlen(stream_name)))) {
|
|
|
slots = pdata->tdm_max_slots;
|
|
|
rate = params_rate(params);
|
|
@@ -349,6 +401,15 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
|
|
|
intf_clk_cfg.clk_attri = CLOCK_ATTRIBUTE_COUPLE_NO;
|
|
|
intf_clk_cfg.clk_root = 0;
|
|
|
|
|
|
+ if (pdata->is_audio_hw_vote_required[index] &&
|
|
|
+ is_fractional_sample_rate(rate)) {
|
|
|
+ ret = mi2s_tdm_hw_vote_req(pdata, 1);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("%s lpass audio hw vote enable failed %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
pr_debug("%s: clk_id :%d clk freq %d\n", __func__,
|
|
|
intf_clk_cfg.clk_id, intf_clk_cfg.clk_freq_in_hz);
|
|
|
ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 1);
|
|
@@ -379,11 +440,21 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
|
|
|
pr_debug("%s: bitwidth set to default : %d\n",
|
|
|
__func__, sample_width);
|
|
|
}
|
|
|
+
|
|
|
intf_clk_cfg.clk_freq_in_hz = rate *
|
|
|
MI2S_NUM_CHANNELS * sample_width;
|
|
|
intf_clk_cfg.clk_attri = CLOCK_ATTRIBUTE_COUPLE_NO;
|
|
|
intf_clk_cfg.clk_root = CLOCK_ROOT_DEFAULT;
|
|
|
|
|
|
+ if (pdata->is_audio_hw_vote_required[index] &&
|
|
|
+ is_fractional_sample_rate(rate)) {
|
|
|
+ ret = mi2s_tdm_hw_vote_req(pdata, 1);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("%s lpass audio hw vote enable failed %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
pr_debug("%s: mi2s clk_id :%d clk freq %d\n", __func__,
|
|
|
intf_clk_cfg.clk_id, intf_clk_cfg.clk_freq_in_hz);
|
|
|
ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 1);
|
|
@@ -396,8 +467,8 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
|
|
|
pr_err("%s: invalid stream name: %s\n", __func__,
|
|
|
stream_name);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
+ atomic_inc(&pdata->lpass_intf_clk_ref_cnt[index]);
|
|
|
done:
|
|
|
mutex_unlock(&pdata->lock[index]);
|
|
|
}
|
|
@@ -448,9 +519,11 @@ void msm_common_snd_shutdown(struct snd_pcm_substream *substream)
|
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
struct snd_soc_card *card = rtd->card;
|
|
|
struct msm_common_pdata *pdata = msm_common_get_pdata(card);
|
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
const char *stream_name = rtd->dai_link->stream_name;
|
|
|
int index = get_intf_index(stream_name);
|
|
|
struct clk_cfg intf_clk_cfg;
|
|
|
+ unsigned int rate = runtime->rate;
|
|
|
|
|
|
memset(&intf_clk_cfg, 0, sizeof(struct clk_cfg));
|
|
|
pr_debug("%s(): substream = %s stream = %d\n", __func__,
|
|
@@ -465,31 +538,46 @@ void msm_common_snd_shutdown(struct snd_pcm_substream *substream)
|
|
|
|
|
|
if (index >= 0) {
|
|
|
mutex_lock(&pdata->lock[index]);
|
|
|
+ atomic_dec(&pdata->lpass_intf_clk_ref_cnt[index]);
|
|
|
+ if (atomic_read(&pdata->lpass_intf_clk_ref_cnt[index]) == 0) {
|
|
|
+ if ((strnstr(stream_name, "TDM", strlen(stream_name)))) {
|
|
|
+ intf_clk_cfg.clk_id = get_tdm_clk_id(index);
|
|
|
+ pr_debug("%s: Disable clock ID: %d\n", __func__, intf_clk_cfg.clk_id);
|
|
|
+ ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ pr_err("%s: prm clk cfg set failed ret %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ } else if((strnstr(stream_name, "MI2S", strlen(stream_name)))) {
|
|
|
+ intf_clk_cfg.clk_id = get_mi2s_clk_id(index);
|
|
|
+ pr_debug("%s: Disable clock ID: %d\n", __func__, intf_clk_cfg.clk_id);
|
|
|
+ ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ pr_err("%s: prm mi2s clk cfg disable failed ret %d\n",
|
|
|
+ __func__, ret);
|
|
|
+ } else {
|
|
|
+ pr_err("%s: invalid stream name: %s\n",
|
|
|
+ __func__, stream_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pdata->is_audio_hw_vote_required[index] &&
|
|
|
+ is_fractional_sample_rate(rate)) {
|
|
|
+ ret = mi2s_tdm_hw_vote_req(pdata, 0);
|
|
|
+ }
|
|
|
+ } else if (atomic_read(&pdata->lpass_intf_clk_ref_cnt[index]) < 0) {
|
|
|
+ atomic_set(&pdata->lpass_intf_clk_ref_cnt[index], 0);
|
|
|
+ }
|
|
|
+
|
|
|
if (pdata->mi2s_gpio_p[index]) {
|
|
|
atomic_dec(&pdata->mi2s_gpio_ref_cnt[index]);
|
|
|
- if (atomic_read(&pdata->mi2s_gpio_ref_cnt[index]) == 0) {
|
|
|
- if ((strnstr(stream_name, "TDM", strlen(stream_name)))) {
|
|
|
- intf_clk_cfg.clk_id = get_tdm_clk_id(index);
|
|
|
- ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 0);
|
|
|
- if (ret < 0)
|
|
|
- pr_err("%s: prm clk cfg set failed ret %d\n",
|
|
|
- __func__, ret);
|
|
|
- } else if((strnstr(stream_name, "MI2S", strlen(stream_name)))) {
|
|
|
- intf_clk_cfg.clk_id = get_mi2s_clk_id(index);
|
|
|
- ret = audio_prm_set_lpass_clk_cfg(&intf_clk_cfg, 0);
|
|
|
- if (ret < 0)
|
|
|
- pr_err("%s: prm mi2s clk cfg disable failed ret %d\n",
|
|
|
- __func__, ret);
|
|
|
- } else {
|
|
|
- pr_err("%s: invalid stream name: %s\n",
|
|
|
- __func__, stream_name);
|
|
|
- }
|
|
|
+ if (atomic_read(&pdata->mi2s_gpio_ref_cnt[index]) == 0) {
|
|
|
ret = msm_cdc_pinctrl_select_sleep_state(
|
|
|
- pdata->mi2s_gpio_p[index]);
|
|
|
+ pdata->mi2s_gpio_p[index]);
|
|
|
if (ret)
|
|
|
dev_err(card->dev,
|
|
|
"%s: pinctrl set actv fail %d\n",
|
|
|
__func__, ret);
|
|
|
+ } else if (atomic_read(&pdata->mi2s_gpio_ref_cnt[index]) < 0) {
|
|
|
+ atomic_set(&pdata->mi2s_gpio_ref_cnt[index], 0);
|
|
|
}
|
|
|
}
|
|
|
mutex_unlock(&pdata->lock[index]);
|
|
@@ -500,6 +588,8 @@ int msm_common_snd_init(struct platform_device *pdev, struct snd_soc_card *card)
|
|
|
{
|
|
|
struct msm_common_pdata *common_pdata = NULL;
|
|
|
int count, ret = 0;
|
|
|
+ uint32_t lpass_audio_hw_vote_required[MI2S_TDM_AUXPCM_MAX] = {0};
|
|
|
+ struct clk *lpass_audio_hw_vote = NULL;
|
|
|
|
|
|
common_pdata = kcalloc(1, sizeof(struct msm_common_pdata), GFP_KERNEL);
|
|
|
if (!common_pdata)
|
|
@@ -523,6 +613,30 @@ int msm_common_snd_init(struct platform_device *pdev, struct snd_soc_card *card)
|
|
|
__func__, common_pdata->tdm_max_slots);
|
|
|
}
|
|
|
|
|
|
+ /* Register LPASS audio hw vote */
|
|
|
+ lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote");
|
|
|
+ if (IS_ERR(lpass_audio_hw_vote)) {
|
|
|
+ ret = PTR_ERR(lpass_audio_hw_vote);
|
|
|
+ dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
|
|
|
+ __func__, "lpass_audio_hw_vote", ret);
|
|
|
+ lpass_audio_hw_vote = NULL;
|
|
|
+ ret = 0;
|
|
|
+ }
|
|
|
+ common_pdata->lpass_audio_hw_vote = lpass_audio_hw_vote;
|
|
|
+
|
|
|
+ ret = of_property_read_u32_array(pdev->dev.of_node,
|
|
|
+ "qcom,mi2s-tdm-is-hw-vote-needed",
|
|
|
+ lpass_audio_hw_vote_required, MI2S_TDM_AUXPCM_MAX);
|
|
|
+ if (ret) {
|
|
|
+ dev_dbg(&pdev->dev, "%s:no qcom,mi2s-tdm-is-hw-vote-needed in DT node\n",
|
|
|
+ __func__);
|
|
|
+ } else {
|
|
|
+ for (count = 0; count < MI2S_TDM_AUXPCM_MAX; count++) {
|
|
|
+ common_pdata->is_audio_hw_vote_required[count] =
|
|
|
+ lpass_audio_hw_vote_required[count];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
common_pdata->mi2s_gpio_p[PRI_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
|
|
|
"qcom,pri-mi2s-gpios", 0);
|
|
|
common_pdata->mi2s_gpio_p[SEC_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
|