Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * spitz.c  --  SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
0004  *
0005  * Copyright 2005 Wolfson Microelectronics PLC.
0006  * Copyright 2005 Openedhand Ltd.
0007  *
0008  * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
0009  *          Richard Purdie <richard@openedhand.com>
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/moduleparam.h>
0014 #include <linux/timer.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/gpio/consumer.h>
0018 #include <sound/core.h>
0019 #include <sound/pcm.h>
0020 #include <sound/soc.h>
0021 
0022 #include <asm/mach-types.h>
0023 #include "../codecs/wm8750.h"
0024 #include "pxa2xx-i2s.h"
0025 
0026 #define SPITZ_HP        0
0027 #define SPITZ_MIC       1
0028 #define SPITZ_LINE      2
0029 #define SPITZ_HEADSET   3
0030 #define SPITZ_HP_OFF    4
0031 #define SPITZ_SPK_ON    0
0032 #define SPITZ_SPK_OFF   1
0033 
0034  /* audio clock in Hz - rounded from 12.235MHz */
0035 #define SPITZ_AUDIO_CLOCK 12288000
0036 
0037 static int spitz_jack_func;
0038 static int spitz_spk_func;
0039 static struct gpio_desc *gpiod_mic, *gpiod_mute_l, *gpiod_mute_r;
0040 
0041 static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
0042 {
0043     snd_soc_dapm_mutex_lock(dapm);
0044 
0045     if (spitz_spk_func == SPITZ_SPK_ON)
0046         snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
0047     else
0048         snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
0049 
0050     /* set up jack connection */
0051     switch (spitz_jack_func) {
0052     case SPITZ_HP:
0053         /* enable and unmute hp jack, disable mic bias */
0054         snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
0055         snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
0056         snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
0057         snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
0058         gpiod_set_value(gpiod_mute_l, 1);
0059         gpiod_set_value(gpiod_mute_r, 1);
0060         break;
0061     case SPITZ_MIC:
0062         /* enable mic jack and bias, mute hp */
0063         snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
0064         snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
0065         snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
0066         snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
0067         gpiod_set_value(gpiod_mute_l, 0);
0068         gpiod_set_value(gpiod_mute_r, 0);
0069         break;
0070     case SPITZ_LINE:
0071         /* enable line jack, disable mic bias and mute hp */
0072         snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
0073         snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
0074         snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
0075         snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
0076         gpiod_set_value(gpiod_mute_l, 0);
0077         gpiod_set_value(gpiod_mute_r, 0);
0078         break;
0079     case SPITZ_HEADSET:
0080         /* enable and unmute headset jack enable mic bias, mute L hp */
0081         snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
0082         snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
0083         snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
0084         snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
0085         gpiod_set_value(gpiod_mute_l, 0);
0086         gpiod_set_value(gpiod_mute_r, 1);
0087         break;
0088     case SPITZ_HP_OFF:
0089 
0090         /* jack removed, everything off */
0091         snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
0092         snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
0093         snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
0094         snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
0095         gpiod_set_value(gpiod_mute_l, 0);
0096         gpiod_set_value(gpiod_mute_r, 0);
0097         break;
0098     }
0099 
0100     snd_soc_dapm_sync_unlocked(dapm);
0101 
0102     snd_soc_dapm_mutex_unlock(dapm);
0103 }
0104 
0105 static int spitz_startup(struct snd_pcm_substream *substream)
0106 {
0107     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0108 
0109     /* check the jack status at stream startup */
0110     spitz_ext_control(&rtd->card->dapm);
0111 
0112     return 0;
0113 }
0114 
0115 static int spitz_hw_params(struct snd_pcm_substream *substream,
0116     struct snd_pcm_hw_params *params)
0117 {
0118     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0119     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0120     struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0121     unsigned int clk = 0;
0122     int ret = 0;
0123 
0124     switch (params_rate(params)) {
0125     case 8000:
0126     case 16000:
0127     case 48000:
0128     case 96000:
0129         clk = 12288000;
0130         break;
0131     case 11025:
0132     case 22050:
0133     case 44100:
0134         clk = 11289600;
0135         break;
0136     }
0137 
0138     /* set the codec system clock for DAC and ADC */
0139     ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
0140         SND_SOC_CLOCK_IN);
0141     if (ret < 0)
0142         return ret;
0143 
0144     /* set the I2S system clock as input (unused) */
0145     ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
0146         SND_SOC_CLOCK_IN);
0147     if (ret < 0)
0148         return ret;
0149 
0150     return 0;
0151 }
0152 
0153 static const struct snd_soc_ops spitz_ops = {
0154     .startup = spitz_startup,
0155     .hw_params = spitz_hw_params,
0156 };
0157 
0158 static int spitz_get_jack(struct snd_kcontrol *kcontrol,
0159     struct snd_ctl_elem_value *ucontrol)
0160 {
0161     ucontrol->value.enumerated.item[0] = spitz_jack_func;
0162     return 0;
0163 }
0164 
0165 static int spitz_set_jack(struct snd_kcontrol *kcontrol,
0166     struct snd_ctl_elem_value *ucontrol)
0167 {
0168     struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
0169 
0170     if (spitz_jack_func == ucontrol->value.enumerated.item[0])
0171         return 0;
0172 
0173     spitz_jack_func = ucontrol->value.enumerated.item[0];
0174     spitz_ext_control(&card->dapm);
0175     return 1;
0176 }
0177 
0178 static int spitz_get_spk(struct snd_kcontrol *kcontrol,
0179     struct snd_ctl_elem_value *ucontrol)
0180 {
0181     ucontrol->value.enumerated.item[0] = spitz_spk_func;
0182     return 0;
0183 }
0184 
0185 static int spitz_set_spk(struct snd_kcontrol *kcontrol,
0186     struct snd_ctl_elem_value *ucontrol)
0187 {
0188     struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
0189 
0190     if (spitz_spk_func == ucontrol->value.enumerated.item[0])
0191         return 0;
0192 
0193     spitz_spk_func = ucontrol->value.enumerated.item[0];
0194     spitz_ext_control(&card->dapm);
0195     return 1;
0196 }
0197 
0198 static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
0199     struct snd_kcontrol *k, int event)
0200 {
0201     gpiod_set_value_cansleep(gpiod_mic, SND_SOC_DAPM_EVENT_ON(event));
0202     return 0;
0203 }
0204 
0205 /* spitz machine dapm widgets */
0206 static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
0207     SND_SOC_DAPM_HP("Headphone Jack", NULL),
0208     SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
0209     SND_SOC_DAPM_SPK("Ext Spk", NULL),
0210     SND_SOC_DAPM_LINE("Line Jack", NULL),
0211 
0212     /* headset is a mic and mono headphone */
0213     SND_SOC_DAPM_HP("Headset Jack", NULL),
0214 };
0215 
0216 /* Spitz machine audio_map */
0217 static const struct snd_soc_dapm_route spitz_audio_map[] = {
0218 
0219     /* headphone connected to LOUT1, ROUT1 */
0220     {"Headphone Jack", NULL, "LOUT1"},
0221     {"Headphone Jack", NULL, "ROUT1"},
0222 
0223     /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
0224     {"Headset Jack", NULL, "ROUT1"},
0225 
0226     /* ext speaker connected to LOUT2, ROUT2  */
0227     {"Ext Spk", NULL, "ROUT2"},
0228     {"Ext Spk", NULL, "LOUT2"},
0229 
0230     /* mic is connected to input 1 - with bias */
0231     {"LINPUT1", NULL, "Mic Bias"},
0232     {"Mic Bias", NULL, "Mic Jack"},
0233 
0234     /* line is connected to input 1 - no bias */
0235     {"LINPUT1", NULL, "Line Jack"},
0236 };
0237 
0238 static const char * const jack_function[] = {"Headphone", "Mic", "Line",
0239     "Headset", "Off"};
0240 static const char * const spk_function[] = {"On", "Off"};
0241 static const struct soc_enum spitz_enum[] = {
0242     SOC_ENUM_SINGLE_EXT(5, jack_function),
0243     SOC_ENUM_SINGLE_EXT(2, spk_function),
0244 };
0245 
0246 static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
0247     SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
0248         spitz_set_jack),
0249     SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
0250         spitz_set_spk),
0251 };
0252 
0253 /* spitz digital audio interface glue - connects codec <--> CPU */
0254 SND_SOC_DAILINK_DEFS(wm8750,
0255     DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
0256     DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
0257     DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
0258 
0259 static struct snd_soc_dai_link spitz_dai = {
0260     .name = "wm8750",
0261     .stream_name = "WM8750",
0262     .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0263            SND_SOC_DAIFMT_CBS_CFS,
0264     .ops = &spitz_ops,
0265     SND_SOC_DAILINK_REG(wm8750),
0266 };
0267 
0268 /* spitz audio machine driver */
0269 static struct snd_soc_card snd_soc_spitz = {
0270     .name = "Spitz",
0271     .owner = THIS_MODULE,
0272     .dai_link = &spitz_dai,
0273     .num_links = 1,
0274 
0275     .controls = wm8750_spitz_controls,
0276     .num_controls = ARRAY_SIZE(wm8750_spitz_controls),
0277     .dapm_widgets = wm8750_dapm_widgets,
0278     .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
0279     .dapm_routes = spitz_audio_map,
0280     .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
0281     .fully_routed = true,
0282 };
0283 
0284 static int spitz_probe(struct platform_device *pdev)
0285 {
0286     struct snd_soc_card *card = &snd_soc_spitz;
0287     int ret;
0288 
0289     gpiod_mic = devm_gpiod_get(&pdev->dev, "mic", GPIOD_OUT_LOW);
0290     if (IS_ERR(gpiod_mic))
0291         return PTR_ERR(gpiod_mic);
0292     gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_LOW);
0293     if (IS_ERR(gpiod_mute_l))
0294         return PTR_ERR(gpiod_mute_l);
0295     gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_LOW);
0296     if (IS_ERR(gpiod_mute_r))
0297         return PTR_ERR(gpiod_mute_r);
0298 
0299     card->dev = &pdev->dev;
0300 
0301     ret = devm_snd_soc_register_card(&pdev->dev, card);
0302     if (ret)
0303         dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
0304             ret);
0305 
0306     return ret;
0307 }
0308 
0309 static int spitz_remove(struct platform_device *pdev)
0310 {
0311     return 0;
0312 }
0313 
0314 static struct platform_driver spitz_driver = {
0315     .driver     = {
0316         .name   = "spitz-audio",
0317         .pm     = &snd_soc_pm_ops,
0318     },
0319     .probe      = spitz_probe,
0320     .remove     = spitz_remove,
0321 };
0322 
0323 module_platform_driver(spitz_driver);
0324 
0325 MODULE_AUTHOR("Richard Purdie");
0326 MODULE_DESCRIPTION("ALSA SoC Spitz");
0327 MODULE_LICENSE("GPL");
0328 MODULE_ALIAS("platform:spitz-audio");