Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // Speyside audio support
0004 //
0005 // Copyright 2011 Wolfson Microelectronics
0006 
0007 #include <sound/soc.h>
0008 #include <sound/soc-dapm.h>
0009 #include <sound/jack.h>
0010 #include <linux/gpio.h>
0011 #include <linux/module.h>
0012 
0013 #include "../codecs/wm8996.h"
0014 #include "../codecs/wm9081.h"
0015 
0016 #define WM8996_HPSEL_GPIO 214
0017 #define MCLK_AUDIO_RATE (512 * 48000)
0018 
0019 static int speyside_set_bias_level(struct snd_soc_card *card,
0020                    struct snd_soc_dapm_context *dapm,
0021                    enum snd_soc_bias_level level)
0022 {
0023     struct snd_soc_pcm_runtime *rtd;
0024     struct snd_soc_dai *codec_dai;
0025     int ret;
0026 
0027     rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
0028     codec_dai = asoc_rtd_to_codec(rtd, 0);
0029 
0030     if (dapm->dev != codec_dai->dev)
0031         return 0;
0032 
0033     switch (level) {
0034     case SND_SOC_BIAS_STANDBY:
0035         ret = snd_soc_dai_set_sysclk(codec_dai, WM8996_SYSCLK_MCLK2,
0036                          32768, SND_SOC_CLOCK_IN);
0037         if (ret < 0)
0038             return ret;
0039 
0040         ret = snd_soc_dai_set_pll(codec_dai, WM8996_FLL_MCLK2,
0041                       0, 0, 0);
0042         if (ret < 0) {
0043             pr_err("Failed to stop FLL\n");
0044             return ret;
0045         }
0046         break;
0047 
0048     default:
0049         break;
0050     }
0051 
0052     return 0;
0053 }
0054 
0055 static int speyside_set_bias_level_post(struct snd_soc_card *card,
0056                     struct snd_soc_dapm_context *dapm,
0057                     enum snd_soc_bias_level level)
0058 {
0059     struct snd_soc_pcm_runtime *rtd;
0060     struct snd_soc_dai *codec_dai;
0061     int ret;
0062 
0063     rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);
0064     codec_dai = asoc_rtd_to_codec(rtd, 0);
0065 
0066     if (dapm->dev != codec_dai->dev)
0067         return 0;
0068 
0069     switch (level) {
0070     case SND_SOC_BIAS_PREPARE:
0071         if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
0072             ret = snd_soc_dai_set_pll(codec_dai, 0,
0073                           WM8996_FLL_MCLK2,
0074                           32768, MCLK_AUDIO_RATE);
0075             if (ret < 0) {
0076                 pr_err("Failed to start FLL\n");
0077                 return ret;
0078             }
0079 
0080             ret = snd_soc_dai_set_sysclk(codec_dai,
0081                              WM8996_SYSCLK_FLL,
0082                              MCLK_AUDIO_RATE,
0083                              SND_SOC_CLOCK_IN);
0084             if (ret < 0)
0085                 return ret;
0086         }
0087         break;
0088 
0089     default:
0090         break;
0091     }
0092 
0093     card->dapm.bias_level = level;
0094 
0095     return 0;
0096 }
0097 
0098 static struct snd_soc_jack speyside_headset;
0099 
0100 /* Headset jack detection DAPM pins */
0101 static struct snd_soc_jack_pin speyside_headset_pins[] = {
0102     {
0103         .pin = "Headset Mic",
0104         .mask = SND_JACK_MICROPHONE,
0105     },
0106 };
0107 
0108 /* Default the headphone selection to active high */
0109 static int speyside_jack_polarity;
0110 
0111 static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
0112                 struct snd_soc_dapm_widget *sink)
0113 {
0114     if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
0115         return 1;
0116     if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
0117         return 1;
0118 
0119     return 0;
0120 }
0121 
0122 static void speyside_set_polarity(struct snd_soc_component *component,
0123                   int polarity)
0124 {
0125     speyside_jack_polarity = !polarity;
0126     gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
0127 
0128     /* Re-run DAPM to make sure we're using the correct mic bias */
0129     snd_soc_dapm_sync(snd_soc_component_get_dapm(component));
0130 }
0131 
0132 static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd)
0133 {
0134     struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
0135     int ret;
0136 
0137     ret = snd_soc_dai_set_sysclk(dai, 0, MCLK_AUDIO_RATE, 0);
0138     if (ret < 0)
0139         return ret;
0140 
0141     return 0;
0142 }
0143 
0144 static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
0145 {
0146     struct snd_soc_dai *dai = asoc_rtd_to_codec(rtd, 0);
0147     struct snd_soc_component *component = dai->component;
0148     int ret;
0149 
0150     ret = snd_soc_dai_set_sysclk(dai, WM8996_SYSCLK_MCLK2, 32768, 0);
0151     if (ret < 0)
0152         return ret;
0153 
0154     ret = gpio_request(WM8996_HPSEL_GPIO, "HP_SEL");
0155     if (ret != 0)
0156         pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
0157     gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
0158 
0159     ret = snd_soc_card_jack_new_pins(rtd->card, "Headset",
0160                      SND_JACK_LINEOUT | SND_JACK_HEADSET |
0161                      SND_JACK_BTN_0,
0162                      &speyside_headset,
0163                      speyside_headset_pins,
0164                      ARRAY_SIZE(speyside_headset_pins));
0165     if (ret)
0166         return ret;
0167 
0168     wm8996_detect(component, &speyside_headset, speyside_set_polarity);
0169 
0170     return 0;
0171 }
0172 
0173 static int speyside_late_probe(struct snd_soc_card *card)
0174 {
0175     snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
0176     snd_soc_dapm_ignore_suspend(&card->dapm, "Headset Mic");
0177     snd_soc_dapm_ignore_suspend(&card->dapm, "Main AMIC");
0178     snd_soc_dapm_ignore_suspend(&card->dapm, "Main DMIC");
0179     snd_soc_dapm_ignore_suspend(&card->dapm, "Main Speaker");
0180     snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Output");
0181     snd_soc_dapm_ignore_suspend(&card->dapm, "WM1250 Input");
0182 
0183     return 0;
0184 }
0185 
0186 static const struct snd_soc_pcm_stream dsp_codec_params = {
0187     .formats = SNDRV_PCM_FMTBIT_S32_LE,
0188     .rate_min = 48000,
0189     .rate_max = 48000,
0190     .channels_min = 2,
0191     .channels_max = 2,
0192 };
0193 
0194 SND_SOC_DAILINK_DEFS(cpu_dsp,
0195     DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
0196     DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
0197     DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
0198 
0199 SND_SOC_DAILINK_DEFS(dsp_codec,
0200     DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
0201     DAILINK_COMP_ARRAY(COMP_CODEC("wm8996.1-001a", "wm8996-aif1")));
0202 
0203 SND_SOC_DAILINK_DEFS(baseband,
0204     DAILINK_COMP_ARRAY(COMP_CPU("wm8996-aif2")),
0205     DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
0206 
0207 static struct snd_soc_dai_link speyside_dai[] = {
0208     {
0209         .name = "CPU-DSP",
0210         .stream_name = "CPU-DSP",
0211         .init = speyside_wm0010_init,
0212         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
0213                 | SND_SOC_DAIFMT_CBM_CFM,
0214         SND_SOC_DAILINK_REG(cpu_dsp),
0215     },
0216     {
0217         .name = "DSP-CODEC",
0218         .stream_name = "DSP-CODEC",
0219         .init = speyside_wm8996_init,
0220         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
0221                 | SND_SOC_DAIFMT_CBM_CFM,
0222         .params = &dsp_codec_params,
0223         .ignore_suspend = 1,
0224         SND_SOC_DAILINK_REG(dsp_codec),
0225     },
0226     {
0227         .name = "Baseband",
0228         .stream_name = "Baseband",
0229         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
0230                 | SND_SOC_DAIFMT_CBM_CFM,
0231         .ignore_suspend = 1,
0232         SND_SOC_DAILINK_REG(baseband),
0233     },
0234 };
0235 
0236 static int speyside_wm9081_init(struct snd_soc_component *component)
0237 {
0238     /* At any time the WM9081 is active it will have this clock */
0239     return snd_soc_component_set_sysclk(component, WM9081_SYSCLK_MCLK, 0,
0240                     MCLK_AUDIO_RATE, 0);
0241 }
0242 
0243 static struct snd_soc_aux_dev speyside_aux_dev[] = {
0244     {
0245         .dlc = COMP_AUX("wm9081.1-006c"),
0246         .init = speyside_wm9081_init,
0247     },
0248 };
0249 
0250 static struct snd_soc_codec_conf speyside_codec_conf[] = {
0251     {
0252         .dlc = COMP_CODEC_CONF("wm9081.1-006c"),
0253         .name_prefix = "Sub",
0254     },
0255 };
0256 
0257 static const struct snd_kcontrol_new controls[] = {
0258     SOC_DAPM_PIN_SWITCH("Main Speaker"),
0259     SOC_DAPM_PIN_SWITCH("Main DMIC"),
0260     SOC_DAPM_PIN_SWITCH("Main AMIC"),
0261     SOC_DAPM_PIN_SWITCH("WM1250 Input"),
0262     SOC_DAPM_PIN_SWITCH("WM1250 Output"),
0263     SOC_DAPM_PIN_SWITCH("Headphone"),
0264 };
0265 
0266 static const struct snd_soc_dapm_widget widgets[] = {
0267     SND_SOC_DAPM_HP("Headphone", NULL),
0268     SND_SOC_DAPM_MIC("Headset Mic", NULL),
0269 
0270     SND_SOC_DAPM_SPK("Main Speaker", NULL),
0271 
0272     SND_SOC_DAPM_MIC("Main AMIC", NULL),
0273     SND_SOC_DAPM_MIC("Main DMIC", NULL),
0274 };
0275 
0276 static const struct snd_soc_dapm_route audio_paths[] = {
0277     { "IN1RN", NULL, "MICB1" },
0278     { "IN1RP", NULL, "MICB1" },
0279     { "IN1RN", NULL, "MICB2" },
0280     { "IN1RP", NULL, "MICB2" },
0281     { "MICB1", NULL, "Headset Mic", speyside_get_micbias },
0282     { "MICB2", NULL, "Headset Mic", speyside_get_micbias },
0283 
0284     { "IN1LP", NULL, "MICB2" },
0285     { "IN1RN", NULL, "MICB1" },
0286     { "MICB2", NULL, "Main AMIC" },
0287 
0288     { "DMIC1DAT", NULL, "MICB1" },
0289     { "DMIC2DAT", NULL, "MICB1" },
0290     { "MICB1", NULL, "Main DMIC" },
0291 
0292     { "Headphone", NULL, "HPOUT1L" },
0293     { "Headphone", NULL, "HPOUT1R" },
0294 
0295     { "Sub IN1", NULL, "HPOUT2L" },
0296     { "Sub IN2", NULL, "HPOUT2R" },
0297 
0298     { "Main Speaker", NULL, "Sub SPKN" },
0299     { "Main Speaker", NULL, "Sub SPKP" },
0300     { "Main Speaker", NULL, "SPKDAT" },
0301 };
0302 
0303 static struct snd_soc_card speyside = {
0304     .name = "Speyside",
0305     .owner = THIS_MODULE,
0306     .dai_link = speyside_dai,
0307     .num_links = ARRAY_SIZE(speyside_dai),
0308     .aux_dev = speyside_aux_dev,
0309     .num_aux_devs = ARRAY_SIZE(speyside_aux_dev),
0310     .codec_conf = speyside_codec_conf,
0311     .num_configs = ARRAY_SIZE(speyside_codec_conf),
0312 
0313     .set_bias_level = speyside_set_bias_level,
0314     .set_bias_level_post = speyside_set_bias_level_post,
0315 
0316     .controls = controls,
0317     .num_controls = ARRAY_SIZE(controls),
0318     .dapm_widgets = widgets,
0319     .num_dapm_widgets = ARRAY_SIZE(widgets),
0320     .dapm_routes = audio_paths,
0321     .num_dapm_routes = ARRAY_SIZE(audio_paths),
0322     .fully_routed = true,
0323 
0324     .late_probe = speyside_late_probe,
0325 };
0326 
0327 static int speyside_probe(struct platform_device *pdev)
0328 {
0329     struct snd_soc_card *card = &speyside;
0330     int ret;
0331 
0332     card->dev = &pdev->dev;
0333 
0334     ret = devm_snd_soc_register_card(&pdev->dev, card);
0335     if (ret)
0336         dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
0337 
0338     return ret;
0339 }
0340 
0341 static struct platform_driver speyside_driver = {
0342     .driver = {
0343         .name = "speyside",
0344         .pm = &snd_soc_pm_ops,
0345     },
0346     .probe = speyside_probe,
0347 };
0348 
0349 module_platform_driver(speyside_driver);
0350 
0351 MODULE_DESCRIPTION("Speyside audio support");
0352 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
0353 MODULE_LICENSE("GPL");
0354 MODULE_ALIAS("platform:speyside");