Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // Midas audio support
0004 //
0005 // Copyright (C) 2018 Simon Shields <simon@lineageos.org>
0006 // Copyright (C) 2020 Samsung Electronics Co., Ltd.
0007 
0008 #include <linux/clk.h>
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/mfd/wm8994/registers.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/of_gpio.h>
0015 #include <linux/regulator/consumer.h>
0016 #include <sound/jack.h>
0017 #include <sound/soc.h>
0018 #include <sound/soc-dapm.h>
0019 
0020 #include "i2s.h"
0021 #include "../codecs/wm8994.h"
0022 
0023 /*
0024  * The MCLK1 clock source is XCLKOUT with its mux set to the external fixed rate
0025  * oscillator (XXTI).
0026  */
0027 #define MCLK1_RATE 24000000U
0028 #define MCLK2_RATE 32768U
0029 #define DEFAULT_FLL1_RATE 11289600U
0030 
0031 struct midas_priv {
0032     struct regulator *reg_mic_bias;
0033     struct regulator *reg_submic_bias;
0034     struct gpio_desc *gpio_fm_sel;
0035     struct gpio_desc *gpio_lineout_sel;
0036     unsigned int fll1_rate;
0037 
0038     struct snd_soc_jack headset_jack;
0039 };
0040 
0041 static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate)
0042 {
0043     struct snd_soc_card *card = rtd->card;
0044     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0045     struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0);
0046     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0047     int ret;
0048 
0049     if (!rate)
0050         rate = priv->fll1_rate;
0051     /*
0052      * If no new rate is requested, set FLL1 to a sane default for jack
0053      * detection.
0054      */
0055     if (!rate)
0056         rate = DEFAULT_FLL1_RATE;
0057 
0058     if (rate != priv->fll1_rate && priv->fll1_rate) {
0059         /* while reconfiguring, switch to MCLK2 for SYSCLK */
0060         ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
0061                          MCLK2_RATE, SND_SOC_CLOCK_IN);
0062         if (ret < 0) {
0063             dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret);
0064             return ret;
0065         }
0066     }
0067 
0068     ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
0069                   MCLK1_RATE, rate);
0070     if (ret < 0) {
0071         dev_err(card->dev, "Failed to set FLL1 rate: %d\n", ret);
0072         return ret;
0073     }
0074     priv->fll1_rate = rate;
0075 
0076     ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1,
0077                      priv->fll1_rate, SND_SOC_CLOCK_IN);
0078     if (ret < 0) {
0079         dev_err(card->dev, "Failed to set SYSCLK source: %d\n", ret);
0080         return ret;
0081     }
0082 
0083     ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, 0,
0084                      SAMSUNG_I2S_OPCLK_PCLK);
0085     if (ret < 0) {
0086         dev_err(card->dev, "Failed to set OPCLK source: %d\n", ret);
0087         return ret;
0088     }
0089 
0090     return 0;
0091 }
0092 
0093 static int midas_stop_fll1(struct snd_soc_pcm_runtime *rtd)
0094 {
0095     struct snd_soc_card *card = rtd->card;
0096     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0097     struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0);
0098     int ret;
0099 
0100     ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2,
0101                      MCLK2_RATE, SND_SOC_CLOCK_IN);
0102     if (ret < 0) {
0103         dev_err(card->dev, "Unable to switch to MCLK2: %d\n", ret);
0104         return ret;
0105     }
0106 
0107     ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0);
0108     if (ret < 0) {
0109         dev_err(card->dev, "Unable to stop FLL1: %d\n", ret);
0110         return ret;
0111     }
0112 
0113     priv->fll1_rate = 0;
0114 
0115     return 0;
0116 }
0117 
0118 static int midas_aif1_hw_params(struct snd_pcm_substream *substream,
0119                 struct snd_pcm_hw_params *params)
0120 {
0121     struct snd_soc_pcm_runtime *rtd = substream->private_data;
0122     unsigned int pll_out;
0123 
0124     /* AIF1CLK should be at least 3MHz for "optimal performance" */
0125     if (params_rate(params) == 8000 || params_rate(params) == 11025)
0126         pll_out = params_rate(params) * 512;
0127     else
0128         pll_out = params_rate(params) * 256;
0129 
0130     return midas_start_fll1(rtd, pll_out);
0131 }
0132 
0133 static const struct snd_soc_ops midas_aif1_ops = {
0134     .hw_params = midas_aif1_hw_params,
0135 };
0136 
0137 /*
0138  * We only have a single external speaker, so mix stereo data
0139  * to a single mono stream.
0140  */
0141 static int midas_ext_spkmode(struct snd_soc_dapm_widget *w,
0142                  struct snd_kcontrol *kcontrol, int event)
0143 {
0144     struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
0145     int ret = 0;
0146 
0147     switch (event) {
0148     case SND_SOC_DAPM_PRE_PMU:
0149         ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS,
0150                   WM8994_SPKMIXR_TO_SPKOUTL_MASK,
0151                   WM8994_SPKMIXR_TO_SPKOUTL);
0152         break;
0153     case SND_SOC_DAPM_POST_PMD:
0154         ret = snd_soc_component_update_bits(codec, WM8994_SPKOUT_MIXERS,
0155                   WM8994_SPKMIXR_TO_SPKOUTL_MASK,
0156                   0);
0157         break;
0158     }
0159 
0160     return ret;
0161 }
0162 
0163 static int midas_mic_bias(struct snd_soc_dapm_widget *w,
0164               struct snd_kcontrol *kcontrol, int event)
0165 {
0166     struct snd_soc_card *card = w->dapm->card;
0167     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0168 
0169     switch (event) {
0170     case SND_SOC_DAPM_PRE_PMU:
0171         return regulator_enable(priv->reg_mic_bias);
0172     case SND_SOC_DAPM_POST_PMD:
0173         return regulator_disable(priv->reg_mic_bias);
0174     }
0175 
0176     return 0;
0177 }
0178 
0179 static int midas_submic_bias(struct snd_soc_dapm_widget *w,
0180                  struct snd_kcontrol *kcontrol, int event)
0181 {
0182     struct snd_soc_card *card = w->dapm->card;
0183     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0184 
0185     switch (event) {
0186     case SND_SOC_DAPM_PRE_PMU:
0187         return regulator_enable(priv->reg_submic_bias);
0188     case SND_SOC_DAPM_POST_PMD:
0189         return regulator_disable(priv->reg_submic_bias);
0190     }
0191 
0192     return 0;
0193 }
0194 
0195 static int midas_fm_set(struct snd_soc_dapm_widget *w,
0196             struct snd_kcontrol *kcontrol, int event)
0197 {
0198     struct snd_soc_card *card = w->dapm->card;
0199     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0200 
0201     if (!priv->gpio_fm_sel)
0202         return 0;
0203 
0204     switch (event) {
0205     case SND_SOC_DAPM_PRE_PMU:
0206         gpiod_set_value_cansleep(priv->gpio_fm_sel, 1);
0207         break;
0208     case SND_SOC_DAPM_POST_PMD:
0209         gpiod_set_value_cansleep(priv->gpio_fm_sel, 0);
0210         break;
0211     }
0212 
0213     return 0;
0214 }
0215 
0216 static int midas_line_set(struct snd_soc_dapm_widget *w,
0217               struct snd_kcontrol *kcontrol, int event)
0218 {
0219     struct snd_soc_card *card = w->dapm->card;
0220     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0221 
0222     if (!priv->gpio_lineout_sel)
0223         return 0;
0224 
0225     switch (event) {
0226     case SND_SOC_DAPM_PRE_PMU:
0227         gpiod_set_value_cansleep(priv->gpio_lineout_sel, 1);
0228         break;
0229     case SND_SOC_DAPM_POST_PMD:
0230         gpiod_set_value_cansleep(priv->gpio_lineout_sel, 0);
0231         break;
0232     }
0233 
0234     return 0;
0235 }
0236 
0237 static const struct snd_kcontrol_new midas_controls[] = {
0238     SOC_DAPM_PIN_SWITCH("HP"),
0239 
0240     SOC_DAPM_PIN_SWITCH("SPK"),
0241     SOC_DAPM_PIN_SWITCH("RCV"),
0242 
0243     SOC_DAPM_PIN_SWITCH("LINE"),
0244     SOC_DAPM_PIN_SWITCH("HDMI"),
0245 
0246     SOC_DAPM_PIN_SWITCH("Main Mic"),
0247     SOC_DAPM_PIN_SWITCH("Sub Mic"),
0248     SOC_DAPM_PIN_SWITCH("Headset Mic"),
0249 
0250     SOC_DAPM_PIN_SWITCH("FM In"),
0251 };
0252 
0253 static const struct snd_soc_dapm_widget midas_dapm_widgets[] = {
0254     SND_SOC_DAPM_HP("HP", NULL),
0255 
0256     SND_SOC_DAPM_SPK("SPK", midas_ext_spkmode),
0257     SND_SOC_DAPM_SPK("RCV", NULL),
0258 
0259     /* FIXME: toggle MAX77693 on i9300/i9305 */
0260     SND_SOC_DAPM_LINE("LINE", midas_line_set),
0261     SND_SOC_DAPM_LINE("HDMI", NULL),
0262     SND_SOC_DAPM_LINE("FM In", midas_fm_set),
0263 
0264     SND_SOC_DAPM_MIC("Headset Mic", NULL),
0265     SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias),
0266     SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias),
0267 };
0268 
0269 static int midas_set_bias_level(struct snd_soc_card *card,
0270                 struct snd_soc_dapm_context *dapm,
0271                 enum snd_soc_bias_level level)
0272 {
0273     struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card,
0274                           &card->dai_link[0]);
0275     struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0);
0276 
0277     if (dapm->dev != aif1_dai->dev)
0278         return 0;
0279 
0280     switch (level) {
0281     case SND_SOC_BIAS_STANDBY:
0282         return midas_stop_fll1(rtd);
0283     case SND_SOC_BIAS_PREPARE:
0284         return midas_start_fll1(rtd, 0);
0285     default:
0286         break;
0287     }
0288 
0289     return 0;
0290 }
0291 
0292 static int midas_late_probe(struct snd_soc_card *card)
0293 {
0294     struct snd_soc_pcm_runtime *rtd = snd_soc_get_pcm_runtime(card,
0295                             &card->dai_link[0]);
0296     struct snd_soc_dai *aif1_dai = asoc_rtd_to_codec(rtd, 0);
0297     struct midas_priv *priv = snd_soc_card_get_drvdata(card);
0298     int ret;
0299 
0300     /* Use MCLK2 as SYSCLK for boot */
0301     ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, MCLK2_RATE,
0302                      SND_SOC_CLOCK_IN);
0303     if (ret < 0) {
0304         dev_err(aif1_dai->dev, "Failed to switch to MCLK2: %d\n", ret);
0305         return ret;
0306     }
0307 
0308     ret = snd_soc_card_jack_new(card, "Headset",
0309             SND_JACK_HEADSET | SND_JACK_MECHANICAL |
0310             SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
0311             SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5,
0312             &priv->headset_jack);
0313     if (ret)
0314         return ret;
0315 
0316     wm8958_mic_detect(aif1_dai->component, &priv->headset_jack,
0317               NULL, NULL, NULL, NULL);
0318     return 0;
0319 }
0320 
0321 static struct snd_soc_dai_driver midas_ext_dai[] = {
0322     {
0323         .name = "Voice call",
0324         .playback = {
0325             .channels_min = 1,
0326             .channels_max = 2,
0327             .rate_min = 8000,
0328             .rate_max = 16000,
0329             .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
0330             .formats = SNDRV_PCM_FMTBIT_S16_LE,
0331         },
0332         .capture = {
0333             .channels_min = 1,
0334             .channels_max = 2,
0335             .rate_min = 8000,
0336             .rate_max = 16000,
0337             .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
0338             .formats = SNDRV_PCM_FMTBIT_S16_LE,
0339         },
0340     },
0341     {
0342         .name = "Bluetooth",
0343         .playback = {
0344             .channels_min = 1,
0345             .channels_max = 2,
0346             .rate_min = 8000,
0347             .rate_max = 16000,
0348             .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
0349             .formats = SNDRV_PCM_FMTBIT_S16_LE,
0350         },
0351         .capture = {
0352             .channels_min = 1,
0353             .channels_max = 2,
0354             .rate_min = 8000,
0355             .rate_max = 16000,
0356             .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
0357             .formats = SNDRV_PCM_FMTBIT_S16_LE,
0358         },
0359     },
0360 };
0361 
0362 static const struct snd_soc_component_driver midas_component = {
0363     .name   = "midas-audio",
0364 };
0365 
0366 SND_SOC_DAILINK_DEFS(wm1811_hifi,
0367     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0368     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
0369     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0370 
0371 SND_SOC_DAILINK_DEFS(wm1811_voice,
0372     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0373     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif2")),
0374     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0375 
0376 SND_SOC_DAILINK_DEFS(wm1811_bt,
0377     DAILINK_COMP_ARRAY(COMP_EMPTY()),
0378     DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif3")),
0379     DAILINK_COMP_ARRAY(COMP_EMPTY()));
0380 
0381 static struct snd_soc_dai_link midas_dai[] = {
0382     {
0383         .name = "WM8994 AIF1",
0384         .stream_name = "HiFi Primary",
0385         .ops = &midas_aif1_ops,
0386         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0387             SND_SOC_DAIFMT_CBM_CFM,
0388         SND_SOC_DAILINK_REG(wm1811_hifi),
0389     }, {
0390         .name = "WM1811 Voice",
0391         .stream_name = "Voice call",
0392         .ignore_suspend = 1,
0393         SND_SOC_DAILINK_REG(wm1811_voice),
0394     }, {
0395         .name = "WM1811 BT",
0396         .stream_name = "Bluetooth",
0397         .ignore_suspend = 1,
0398         SND_SOC_DAILINK_REG(wm1811_bt),
0399     },
0400 };
0401 
0402 static struct snd_soc_card midas_card = {
0403     .name = "Midas WM1811",
0404     .owner = THIS_MODULE,
0405 
0406     .dai_link = midas_dai,
0407     .num_links = ARRAY_SIZE(midas_dai),
0408     .controls = midas_controls,
0409     .num_controls = ARRAY_SIZE(midas_controls),
0410     .dapm_widgets = midas_dapm_widgets,
0411     .num_dapm_widgets = ARRAY_SIZE(midas_dapm_widgets),
0412 
0413     .set_bias_level = midas_set_bias_level,
0414     .late_probe = midas_late_probe,
0415 };
0416 
0417 static int midas_probe(struct platform_device *pdev)
0418 {
0419     struct device_node *cpu_dai_node = NULL, *codec_dai_node = NULL;
0420     struct device_node *cpu = NULL, *codec = NULL;
0421     struct snd_soc_card *card = &midas_card;
0422     struct device *dev = &pdev->dev;
0423     static struct snd_soc_dai_link *dai_link;
0424     struct midas_priv *priv;
0425     int ret, i;
0426 
0427     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0428     if (!priv)
0429         return -ENOMEM;
0430 
0431     snd_soc_card_set_drvdata(card, priv);
0432     card->dev = dev;
0433 
0434     priv->reg_mic_bias = devm_regulator_get(dev, "mic-bias");
0435     if (IS_ERR(priv->reg_mic_bias)) {
0436         dev_err(dev, "Failed to get mic bias regulator\n");
0437         return PTR_ERR(priv->reg_mic_bias);
0438     }
0439 
0440     priv->reg_submic_bias = devm_regulator_get(dev, "submic-bias");
0441     if (IS_ERR(priv->reg_submic_bias)) {
0442         dev_err(dev, "Failed to get submic bias regulator\n");
0443         return PTR_ERR(priv->reg_submic_bias);
0444     }
0445 
0446     priv->gpio_fm_sel = devm_gpiod_get_optional(dev, "fm-sel", GPIOD_OUT_HIGH);
0447     if (IS_ERR(priv->gpio_fm_sel)) {
0448         dev_err(dev, "Failed to get FM selection GPIO\n");
0449         return PTR_ERR(priv->gpio_fm_sel);
0450     }
0451 
0452     priv->gpio_lineout_sel = devm_gpiod_get_optional(dev, "lineout-sel",
0453                             GPIOD_OUT_HIGH);
0454     if (IS_ERR(priv->gpio_lineout_sel)) {
0455         dev_err(dev, "Failed to get line out selection GPIO\n");
0456         return PTR_ERR(priv->gpio_lineout_sel);
0457     }
0458 
0459     ret = snd_soc_of_parse_card_name(card, "model");
0460     if (ret < 0) {
0461         dev_err(dev, "Card name is not specified\n");
0462         return ret;
0463     }
0464 
0465     ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
0466     if (ret < 0) {
0467         dev_err(dev, "Audio routing invalid/unspecified\n");
0468         return ret;
0469     }
0470 
0471     cpu = of_get_child_by_name(dev->of_node, "cpu");
0472     if (!cpu)
0473         return -EINVAL;
0474 
0475     codec = of_get_child_by_name(dev->of_node, "codec");
0476     if (!codec) {
0477         of_node_put(cpu);
0478         return -EINVAL;
0479     }
0480 
0481     cpu_dai_node = of_parse_phandle(cpu, "sound-dai", 0);
0482     of_node_put(cpu);
0483     if (!cpu_dai_node) {
0484         dev_err(dev, "parsing cpu/sound-dai failed\n");
0485         of_node_put(codec);
0486         return -EINVAL;
0487     }
0488 
0489     codec_dai_node = of_parse_phandle(codec, "sound-dai", 0);
0490     of_node_put(codec);
0491     if (!codec_dai_node) {
0492         dev_err(dev, "audio-codec property invalid/missing\n");
0493         ret = -EINVAL;
0494         goto put_cpu_dai_node;
0495     }
0496 
0497     for_each_card_prelinks(card, i, dai_link) {
0498         dai_link->codecs->of_node = codec_dai_node;
0499         dai_link->cpus->of_node = cpu_dai_node;
0500         dai_link->platforms->of_node = cpu_dai_node;
0501     }
0502 
0503     ret = devm_snd_soc_register_component(dev, &midas_component,
0504             midas_ext_dai, ARRAY_SIZE(midas_ext_dai));
0505     if (ret < 0) {
0506         dev_err(dev, "Failed to register component: %d\n", ret);
0507         goto put_codec_dai_node;
0508     }
0509 
0510     ret = devm_snd_soc_register_card(dev, card);
0511     if (ret < 0) {
0512         dev_err(dev, "Failed to register card: %d\n", ret);
0513         goto put_codec_dai_node;
0514     }
0515 
0516     return 0;
0517 
0518 put_codec_dai_node:
0519     of_node_put(codec_dai_node);
0520 put_cpu_dai_node:
0521     of_node_put(cpu_dai_node);
0522     return ret;
0523 }
0524 
0525 static const struct of_device_id midas_of_match[] = {
0526     { .compatible = "samsung,midas-audio" },
0527     { },
0528 };
0529 MODULE_DEVICE_TABLE(of, midas_of_match);
0530 
0531 static struct platform_driver midas_driver = {
0532     .driver = {
0533         .name = "midas-audio",
0534         .of_match_table = midas_of_match,
0535         .pm = &snd_soc_pm_ops,
0536     },
0537     .probe = midas_probe,
0538 };
0539 module_platform_driver(midas_driver);
0540 
0541 MODULE_AUTHOR("Simon Shields <simon@lineageos.org>");
0542 MODULE_DESCRIPTION("ASoC support for Midas");
0543 MODULE_LICENSE("GPL v2");