OSDN Git Service

ASoC: msm8998: Add DoP support on QUAT TDM
authorKarthikeyan Mani <kmani@codeaurora.org>
Fri, 13 Jan 2017 05:14:08 +0000 (21:14 -0800)
committerGerrit - the friendly Code Review server <code-review@localhost>
Mon, 13 Feb 2017 09:10:05 +0000 (01:10 -0800)
Add pinctrl dynamic switching support between
QUAT TDM and QUAT MI2S. Add snd_soc_be_ops for
QUAT TDM.

CRs-fixed: 1116515
Change-Id: Iced075d43d137639bcda6b6e6b2233b6740c5d36
Signed-off-by: Karthikeyan Mani <kmani@codeaurora.org>
sound/soc/msm/msm8998.c

index 83ae4c1..e6fa114 100644 (file)
@@ -159,6 +159,21 @@ struct msm_wsa881x_dev_info {
        u32 index;
 };
 
+enum pinctrl_pin_state {
+       STATE_DISABLE = 0, /* All pins are in sleep state */
+       STATE_MI2S_ACTIVE,  /* IS2 = active, TDM = sleep */
+       STATE_TDM_ACTIVE,  /* IS2 = sleep, TDM = active */
+};
+
+struct msm_pinctrl_info {
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *mi2s_disable;
+       struct pinctrl_state *tdm_disable;
+       struct pinctrl_state *mi2s_active;
+       struct pinctrl_state *tdm_active;
+       enum pinctrl_pin_state curr_state;
+};
+
 struct msm_asoc_mach_data {
        u32 mclk_freq;
        int us_euro_gpio; /* used by gpio driver API */
@@ -166,6 +181,7 @@ struct msm_asoc_mach_data {
        struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
        struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
        struct snd_info_entry *codec_root;
+       struct msm_pinctrl_info pinctrl_info;
 };
 
 struct msm_asoc_wcd93xx_codec {
@@ -174,6 +190,9 @@ struct msm_asoc_wcd93xx_codec {
        void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
 };
 
+static const char *const pin_states[] = {"sleep", "i2s-active",
+                                        "tdm-active"};
+
 enum {
        TDM_0 = 0,
        TDM_1,
@@ -3993,6 +4012,275 @@ done:
        return ret;
 }
 
+static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info,
+                               enum pinctrl_pin_state new_state)
+{
+       int ret = 0;
+       int curr_state = 0;
+
+       if (pinctrl_info == NULL) {
+               pr_err("%s: pinctrl_info is NULL\n", __func__);
+               ret = -EINVAL;
+               goto err;
+       }
+       curr_state = pinctrl_info->curr_state;
+       pinctrl_info->curr_state = new_state;
+       pr_debug("%s: curr_state = %s new_state = %s\n", __func__,
+                pin_states[curr_state], pin_states[pinctrl_info->curr_state]);
+
+       if (curr_state == pinctrl_info->curr_state) {
+               pr_debug("%s: Already in same state\n", __func__);
+               goto err;
+       }
+
+       if (curr_state != STATE_DISABLE &&
+               pinctrl_info->curr_state != STATE_DISABLE) {
+               pr_debug("%s: state already active cannot switch\n", __func__);
+               ret = -EIO;
+               goto err;
+       }
+
+       switch (pinctrl_info->curr_state) {
+       case STATE_MI2S_ACTIVE:
+               ret = pinctrl_select_state(pinctrl_info->pinctrl,
+                                       pinctrl_info->mi2s_active);
+               if (ret) {
+                       pr_err("%s: MI2S state select failed with %d\n",
+                               __func__, ret);
+                       ret = -EIO;
+                       goto err;
+               }
+               break;
+       case STATE_TDM_ACTIVE:
+               ret = pinctrl_select_state(pinctrl_info->pinctrl,
+                                       pinctrl_info->tdm_active);
+               if (ret) {
+                       pr_err("%s: TDM state select failed with %d\n",
+                               __func__, ret);
+                       ret = -EIO;
+                       goto err;
+               }
+               break;
+       case STATE_DISABLE:
+               if (curr_state == STATE_MI2S_ACTIVE) {
+                       ret = pinctrl_select_state(pinctrl_info->pinctrl,
+                                       pinctrl_info->mi2s_disable);
+               } else {
+                       ret = pinctrl_select_state(pinctrl_info->pinctrl,
+                                       pinctrl_info->tdm_disable);
+               }
+               if (ret) {
+                       pr_err("%s:  state disable failed with %d\n",
+                               __func__, ret);
+                       ret = -EIO;
+                       goto err;
+               }
+               break;
+       default:
+               pr_err("%s: TLMM pin state is invalid\n", __func__);
+               return -EINVAL;
+       }
+
+err:
+       return ret;
+}
+
+static void msm_release_pinctrl(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+       struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+       if (pinctrl_info->pinctrl) {
+               devm_pinctrl_put(pinctrl_info->pinctrl);
+               pinctrl_info->pinctrl = NULL;
+       }
+}
+
+static int msm_get_pinctrl(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+       struct msm_pinctrl_info *pinctrl_info = NULL;
+       struct pinctrl *pinctrl;
+       int ret;
+
+       pinctrl_info = &pdata->pinctrl_info;
+
+       if (pinctrl_info == NULL) {
+               pr_err("%s: pinctrl_info is NULL\n", __func__);
+               return -EINVAL;
+       }
+
+       pinctrl = devm_pinctrl_get(&pdev->dev);
+       if (IS_ERR_OR_NULL(pinctrl)) {
+               pr_err("%s: Unable to get pinctrl handle\n", __func__);
+               return -EINVAL;
+       }
+       pinctrl_info->pinctrl = pinctrl;
+
+       /* get all the states handles from Device Tree */
+       pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl,
+                                               "quat-mi2s-sleep");
+       if (IS_ERR(pinctrl_info->mi2s_disable)) {
+               pr_err("%s: could not get mi2s_disable pinstate\n", __func__);
+               goto err;
+       }
+       pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl,
+                                               "quat-mi2s-active");
+       if (IS_ERR(pinctrl_info->mi2s_active)) {
+               pr_err("%s: could not get mi2s_active pinstate\n", __func__);
+               goto err;
+       }
+       pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl,
+                                               "quat-tdm-sleep");
+       if (IS_ERR(pinctrl_info->tdm_disable)) {
+               pr_err("%s: could not get tdm_disable pinstate\n", __func__);
+               goto err;
+       }
+       pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl,
+                                               "quat-tdm-active");
+       if (IS_ERR(pinctrl_info->tdm_active)) {
+               pr_err("%s: could not get tdm_active pinstate\n",
+                       __func__);
+               goto err;
+       }
+       /* Reset the TLMM pins to a default state */
+       ret = pinctrl_select_state(pinctrl_info->pinctrl,
+                                       pinctrl_info->mi2s_disable);
+       if (ret != 0) {
+               pr_err("%s: Disable TLMM pins failed with %d\n",
+                       __func__, ret);
+               ret = -EIO;
+               goto err;
+       }
+       pinctrl_info->curr_state = STATE_DISABLE;
+
+       return 0;
+
+err:
+       devm_pinctrl_put(pinctrl);
+       pinctrl_info->pinctrl = NULL;
+       return -EINVAL;
+}
+
+static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                                     struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_interval *rate = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) {
+               channels->min = channels->max =
+                               tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+               param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+                              tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+               rate->min = rate->max =
+                               tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+       } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) {
+               channels->min = channels->max =
+                               tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+               param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+                              tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+               rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+       } else {
+               pr_err("%s: dai id 0x%x not supported\n",
+                       __func__, cpu_dai->id);
+               return -EINVAL;
+       }
+
+       pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+               __func__, cpu_dai->id, channels->max, rate->max,
+               params_format(params));
+
+       return 0;
+}
+
+static int msm8998_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int ret = 0;
+       int channels, slot_width, slots;
+       unsigned int slot_mask;
+       unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28};
+
+       pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+       slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+       /*2 slot config - bits 0 and 1 set for the first two slots */
+       slot_mask = 0x0000FFFF >> (16-slots);
+       slot_width = 32;
+       channels = slots;
+
+       pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               pr_debug("%s: slot_width %d\n", __func__, slot_width);
+               ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+                       slots, slot_width);
+               if (ret < 0) {
+                       pr_err("%s: failed to set tdm slot, err:%d\n",
+                               __func__, ret);
+                       goto end;
+               }
+
+               ret = snd_soc_dai_set_channel_map(cpu_dai,
+                       0, NULL, channels, slot_offset);
+               if (ret < 0) {
+                       pr_err("%s: failed to set channel map, err:%d\n",
+                               __func__, ret);
+                       goto end;
+               }
+       } else {
+               pr_err("%s: invalid use case, err:%d\n",
+                       __func__, ret);
+       }
+
+end:
+       return ret;
+}
+
+static int msm8998_tdm_snd_startup(struct snd_pcm_substream *substream)
+{
+       int ret = 0;
+       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);
+       struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+       ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE);
+       if (ret)
+               pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+                       __func__, ret);
+
+       return ret;
+}
+
+static void msm8998_tdm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+       int ret = 0;
+       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);
+       struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
+
+       ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+       if (ret)
+               pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+                       __func__, ret);
+
+}
+
+static struct snd_soc_ops msm8998_tdm_be_ops = {
+       .hw_params = msm8998_tdm_snd_hw_params,
+       .startup = msm8998_tdm_snd_startup,
+       .shutdown = msm8998_tdm_snd_shutdown
+};
+
 static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
 {
        int ret = 0;
@@ -4000,6 +4288,9 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int index = cpu_dai->id;
        unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+       struct snd_soc_card *card = rtd->card;
+       struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+       struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
 
        dev_dbg(rtd->card->dev,
                "%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
@@ -4013,6 +4304,15 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
                        __func__, cpu_dai->id);
                goto done;
        }
+       if (index == QUAT_MI2S) {
+               ret = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE);
+               if (ret) {
+                       pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+                               __func__, ret);
+                       goto done;
+               }
+       }
+
        /*
         * Muxtex protection in case the same MI2S
         * interface using for both TX and RX  so
@@ -4065,6 +4365,9 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
        int ret;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        int index = rtd->cpu_dai->id;
+       struct snd_soc_card *card = rtd->card;
+       struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+       struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info;
 
        pr_debug("%s(): substream = %s  stream = %d\n", __func__,
                 substream->name, substream->stream);
@@ -4083,6 +4386,13 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
                }
        }
        mutex_unlock(&mi2s_intf_conf[index].lock);
+
+       if (index == QUAT_MI2S) {
+               ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE);
+               if (ret)
+                       pr_err("%s: MI2S TLMM pinctrl set failed with %d\n",
+                               __func__, ret);
+       }
 }
 
 static struct snd_soc_ops msm_mi2s_be_ops = {
@@ -5210,8 +5520,8 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = {
                .no_pcm = 1,
                .dpcm_playback = 1,
                .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
-               .be_hw_params_fixup = msm_be_hw_params_fixup,
-               .ops = &msm_tdm_be_ops,
+               .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+               .ops = &msm8998_tdm_be_ops,
                .ignore_suspend = 1,
        },
        {
@@ -6883,6 +7193,17 @@ static int msm_asoc_machine_probe(struct platform_device *pdev)
                dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
                        ret);
 
+       /* Parse pinctrl info from devicetree */
+       ret = msm_get_pinctrl(pdev);
+       if (!ret) {
+               pr_debug("%s: pinctrl parsing successful\n", __func__);
+       } else {
+               dev_dbg(&pdev->dev,
+                       "%s: Parsing pinctrl failed with %d. Cannot use Ports\n",
+                       __func__, ret);
+               ret = 0;
+       }
+
        i2s_auxpcm_init(pdev);
 
        is_initial_boot = true;
@@ -6900,6 +7221,7 @@ err:
                gpio_free(pdata->us_euro_gpio);
                pdata->us_euro_gpio = 0;
        }
+       msm_release_pinctrl(pdev);
        devm_kfree(&pdev->dev, pdata);
        return ret;
 }