Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
0004 //
0005 // Based on smdk6410_wm8987.c
0006 //     Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
0007 //     Graeme Gregory - graeme.gregory@wolfsonmicro.com
0008 
0009 #include <linux/gpio/consumer.h>
0010 #include <linux/module.h>
0011 
0012 #include <sound/soc.h>
0013 #include <sound/jack.h>
0014 
0015 #include "i2s.h"
0016 #include "../codecs/wm8750.h"
0017 
0018 /*
0019  * WM8987 is register compatible with WM8750, so using that as base driver.
0020  */
0021 
0022 static struct snd_soc_card snd_soc_smartq;
0023 
0024 static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
0025     struct snd_pcm_hw_params *params)
0026 {
0027     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0028     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0029     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0030     unsigned int clk = 0;
0031     int ret;
0032 
0033     switch (params_rate(params)) {
0034     case 8000:
0035     case 16000:
0036     case 32000:
0037     case 48000:
0038     case 96000:
0039         clk = 12288000;
0040         break;
0041     case 11025:
0042     case 22050:
0043     case 44100:
0044     case 88200:
0045         clk = 11289600;
0046         break;
0047     }
0048 
0049     /* Use PCLK for I2S signal generation */
0050     ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
0051                     0, SND_SOC_CLOCK_IN);
0052     if (ret < 0)
0053         return ret;
0054 
0055     /* Gate the RCLK output on PAD */
0056     ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
0057                     0, SND_SOC_CLOCK_IN);
0058     if (ret < 0)
0059         return ret;
0060 
0061     /* set the codec system clock for DAC and ADC */
0062     ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
0063                      SND_SOC_CLOCK_IN);
0064     if (ret < 0)
0065         return ret;
0066 
0067     return 0;
0068 }
0069 
0070 /*
0071  * SmartQ WM8987 HiFi DAI operations.
0072  */
0073 static const struct snd_soc_ops smartq_hifi_ops = {
0074     .hw_params = smartq_hifi_hw_params,
0075 };
0076 
0077 static struct snd_soc_jack smartq_jack;
0078 
0079 static struct snd_soc_jack_pin smartq_jack_pins[] = {
0080     /* Disable speaker when headphone is plugged in */
0081     {
0082         .pin    = "Internal Speaker",
0083         .mask   = SND_JACK_HEADPHONE,
0084     },
0085 };
0086 
0087 static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
0088     {
0089         .gpio       = -1,
0090         .name       = "headphone detect",
0091         .report     = SND_JACK_HEADPHONE,
0092         .debounce_time  = 200,
0093     },
0094 };
0095 
0096 static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
0097     SOC_DAPM_PIN_SWITCH("Internal Speaker"),
0098     SOC_DAPM_PIN_SWITCH("Headphone Jack"),
0099     SOC_DAPM_PIN_SWITCH("Internal Mic"),
0100 };
0101 
0102 static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
0103                 struct snd_kcontrol *k,
0104                 int event)
0105 {
0106     struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq);
0107 
0108     gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event));
0109 
0110     return 0;
0111 }
0112 
0113 static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
0114     SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
0115     SND_SOC_DAPM_HP("Headphone Jack", NULL),
0116     SND_SOC_DAPM_MIC("Internal Mic", NULL),
0117 };
0118 
0119 static const struct snd_soc_dapm_route audio_map[] = {
0120     {"Headphone Jack", NULL, "LOUT2"},
0121     {"Headphone Jack", NULL, "ROUT2"},
0122 
0123     {"Internal Speaker", NULL, "LOUT2"},
0124     {"Internal Speaker", NULL, "ROUT2"},
0125 
0126     {"Mic Bias", NULL, "Internal Mic"},
0127     {"LINPUT2", NULL, "Mic Bias"},
0128 };
0129 
0130 static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
0131 {
0132     struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
0133     int err = 0;
0134 
0135     /* set endpoints to not connected */
0136     snd_soc_dapm_nc_pin(dapm, "LINPUT1");
0137     snd_soc_dapm_nc_pin(dapm, "RINPUT1");
0138     snd_soc_dapm_nc_pin(dapm, "OUT3");
0139     snd_soc_dapm_nc_pin(dapm, "ROUT1");
0140 
0141     /* Headphone jack detection */
0142     err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
0143                      SND_JACK_HEADPHONE, &smartq_jack,
0144                      smartq_jack_pins,
0145                      ARRAY_SIZE(smartq_jack_pins));
0146     if (err)
0147         return err;
0148 
0149     err = snd_soc_jack_add_gpios(&smartq_jack,
0150                      ARRAY_SIZE(smartq_jack_gpios),
0151                      smartq_jack_gpios);
0152 
0153     return err;
0154 }
0155 
0156 SND_SOC_DAILINK_DEFS(wm8987,
0157     DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
0158     DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-0x1a", "wm8750-hifi")),
0159     DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
0160 
0161 static struct snd_soc_dai_link smartq_dai[] = {
0162     {
0163         .name       = "wm8987",
0164         .stream_name    = "SmartQ Hi-Fi",
0165         .init       = smartq_wm8987_init,
0166         .dai_fmt    = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0167                   SND_SOC_DAIFMT_CBS_CFS,
0168         .ops        = &smartq_hifi_ops,
0169         SND_SOC_DAILINK_REG(wm8987),
0170     },
0171 };
0172 
0173 static struct snd_soc_card snd_soc_smartq = {
0174     .name = "SmartQ",
0175     .owner = THIS_MODULE,
0176     .dai_link = smartq_dai,
0177     .num_links = ARRAY_SIZE(smartq_dai),
0178 
0179     .dapm_widgets = wm8987_dapm_widgets,
0180     .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
0181     .dapm_routes = audio_map,
0182     .num_dapm_routes = ARRAY_SIZE(audio_map),
0183     .controls = wm8987_smartq_controls,
0184     .num_controls = ARRAY_SIZE(wm8987_smartq_controls),
0185 };
0186 
0187 static int smartq_probe(struct platform_device *pdev)
0188 {
0189     struct gpio_desc *gpio;
0190     int ret;
0191 
0192     platform_set_drvdata(pdev, &snd_soc_smartq);
0193 
0194     /* Initialise GPIOs used by amplifiers */
0195     gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown",
0196                   GPIOD_OUT_HIGH);
0197     if (IS_ERR(gpio)) {
0198         dev_err(&pdev->dev, "Failed to register GPK12\n");
0199         ret = PTR_ERR(gpio);
0200         goto out;
0201     }
0202     snd_soc_card_set_drvdata(&snd_soc_smartq, gpio);
0203 
0204     ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq);
0205     if (ret)
0206         dev_err(&pdev->dev, "Failed to register card\n");
0207 
0208 out:
0209     return ret;
0210 }
0211 
0212 static struct platform_driver smartq_driver = {
0213     .driver = {
0214         .name = "smartq-audio",
0215     },
0216     .probe = smartq_probe,
0217 };
0218 
0219 module_platform_driver(smartq_driver);
0220 
0221 /* Module information */
0222 MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
0223 MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
0224 MODULE_LICENSE("GPL");