1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita 4 * 5 * Copyright 2005 Wolfson Microelectronics PLC. 6 * Copyright 2005 Openedhand Ltd. 7 * 8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 9 * Richard Purdie <richard@openedhand.com> 10 */ 11 12 #include <linux/module.h> 13 #include <linux/moduleparam.h> 14 #include <linux/timer.h> 15 #include <linux/interrupt.h> 16 #include <linux/platform_device.h> 17 #include <linux/gpio.h> 18 #include <sound/core.h> 19 #include <sound/pcm.h> 20 #include <sound/soc.h> 21 22 #include <asm/mach-types.h> 23 #include <mach/spitz.h> 24 #include "../codecs/wm8750.h" 25 #include "pxa2xx-i2s.h" 26 27 #define SPITZ_HP 0 28 #define SPITZ_MIC 1 29 #define SPITZ_LINE 2 30 #define SPITZ_HEADSET 3 31 #define SPITZ_HP_OFF 4 32 #define SPITZ_SPK_ON 0 33 #define SPITZ_SPK_OFF 1 34 35 /* audio clock in Hz - rounded from 12.235MHz */ 36 #define SPITZ_AUDIO_CLOCK 12288000 37 38 static int spitz_jack_func; 39 static int spitz_spk_func; 40 static int spitz_mic_gpio; 41 42 static void spitz_ext_control(struct snd_soc_dapm_context *dapm) 43 { 44 snd_soc_dapm_mutex_lock(dapm); 45 46 if (spitz_spk_func == SPITZ_SPK_ON) 47 snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); 48 else 49 snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); 50 51 /* set up jack connection */ 52 switch (spitz_jack_func) { 53 case SPITZ_HP: 54 /* enable and unmute hp jack, disable mic bias */ 55 snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 56 snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 57 snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 58 snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); 59 gpio_set_value(SPITZ_GPIO_MUTE_L, 1); 60 gpio_set_value(SPITZ_GPIO_MUTE_R, 1); 61 break; 62 case SPITZ_MIC: 63 /* enable mic jack and bias, mute hp */ 64 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 65 snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 66 snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 67 snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); 68 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 69 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 70 break; 71 case SPITZ_LINE: 72 /* enable line jack, disable mic bias and mute hp */ 73 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 74 snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 75 snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 76 snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack"); 77 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 78 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 79 break; 80 case SPITZ_HEADSET: 81 /* enable and unmute headset jack enable mic bias, mute L hp */ 82 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 83 snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack"); 84 snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 85 snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack"); 86 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 87 gpio_set_value(SPITZ_GPIO_MUTE_R, 1); 88 break; 89 case SPITZ_HP_OFF: 90 91 /* jack removed, everything off */ 92 snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); 93 snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack"); 94 snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack"); 95 snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack"); 96 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 97 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 98 break; 99 } 100 101 snd_soc_dapm_sync_unlocked(dapm); 102 103 snd_soc_dapm_mutex_unlock(dapm); 104 } 105 106 static int spitz_startup(struct snd_pcm_substream *substream) 107 { 108 struct snd_soc_pcm_runtime *rtd = substream->private_data; 109 110 /* check the jack status at stream startup */ 111 spitz_ext_control(&rtd->card->dapm); 112 113 return 0; 114 } 115 116 static int spitz_hw_params(struct snd_pcm_substream *substream, 117 struct snd_pcm_hw_params *params) 118 { 119 struct snd_soc_pcm_runtime *rtd = substream->private_data; 120 struct snd_soc_dai *codec_dai = rtd->codec_dai; 121 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 122 unsigned int clk = 0; 123 int ret = 0; 124 125 switch (params_rate(params)) { 126 case 8000: 127 case 16000: 128 case 48000: 129 case 96000: 130 clk = 12288000; 131 break; 132 case 11025: 133 case 22050: 134 case 44100: 135 clk = 11289600; 136 break; 137 } 138 139 /* set the codec system clock for DAC and ADC */ 140 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 141 SND_SOC_CLOCK_IN); 142 if (ret < 0) 143 return ret; 144 145 /* set the I2S system clock as input (unused) */ 146 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 147 SND_SOC_CLOCK_IN); 148 if (ret < 0) 149 return ret; 150 151 return 0; 152 } 153 154 static const struct snd_soc_ops spitz_ops = { 155 .startup = spitz_startup, 156 .hw_params = spitz_hw_params, 157 }; 158 159 static int spitz_get_jack(struct snd_kcontrol *kcontrol, 160 struct snd_ctl_elem_value *ucontrol) 161 { 162 ucontrol->value.enumerated.item[0] = spitz_jack_func; 163 return 0; 164 } 165 166 static int spitz_set_jack(struct snd_kcontrol *kcontrol, 167 struct snd_ctl_elem_value *ucontrol) 168 { 169 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 170 171 if (spitz_jack_func == ucontrol->value.enumerated.item[0]) 172 return 0; 173 174 spitz_jack_func = ucontrol->value.enumerated.item[0]; 175 spitz_ext_control(&card->dapm); 176 return 1; 177 } 178 179 static int spitz_get_spk(struct snd_kcontrol *kcontrol, 180 struct snd_ctl_elem_value *ucontrol) 181 { 182 ucontrol->value.enumerated.item[0] = spitz_spk_func; 183 return 0; 184 } 185 186 static int spitz_set_spk(struct snd_kcontrol *kcontrol, 187 struct snd_ctl_elem_value *ucontrol) 188 { 189 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 190 191 if (spitz_spk_func == ucontrol->value.enumerated.item[0]) 192 return 0; 193 194 spitz_spk_func = ucontrol->value.enumerated.item[0]; 195 spitz_ext_control(&card->dapm); 196 return 1; 197 } 198 199 static int spitz_mic_bias(struct snd_soc_dapm_widget *w, 200 struct snd_kcontrol *k, int event) 201 { 202 gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event)); 203 return 0; 204 } 205 206 /* spitz machine dapm widgets */ 207 static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 208 SND_SOC_DAPM_HP("Headphone Jack", NULL), 209 SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), 210 SND_SOC_DAPM_SPK("Ext Spk", NULL), 211 SND_SOC_DAPM_LINE("Line Jack", NULL), 212 213 /* headset is a mic and mono headphone */ 214 SND_SOC_DAPM_HP("Headset Jack", NULL), 215 }; 216 217 /* Spitz machine audio_map */ 218 static const struct snd_soc_dapm_route spitz_audio_map[] = { 219 220 /* headphone connected to LOUT1, ROUT1 */ 221 {"Headphone Jack", NULL, "LOUT1"}, 222 {"Headphone Jack", NULL, "ROUT1"}, 223 224 /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ 225 {"Headset Jack", NULL, "ROUT1"}, 226 227 /* ext speaker connected to LOUT2, ROUT2 */ 228 {"Ext Spk", NULL, "ROUT2"}, 229 {"Ext Spk", NULL, "LOUT2"}, 230 231 /* mic is connected to input 1 - with bias */ 232 {"LINPUT1", NULL, "Mic Bias"}, 233 {"Mic Bias", NULL, "Mic Jack"}, 234 235 /* line is connected to input 1 - no bias */ 236 {"LINPUT1", NULL, "Line Jack"}, 237 }; 238 239 static const char * const jack_function[] = {"Headphone", "Mic", "Line", 240 "Headset", "Off"}; 241 static const char * const spk_function[] = {"On", "Off"}; 242 static const struct soc_enum spitz_enum[] = { 243 SOC_ENUM_SINGLE_EXT(5, jack_function), 244 SOC_ENUM_SINGLE_EXT(2, spk_function), 245 }; 246 247 static const struct snd_kcontrol_new wm8750_spitz_controls[] = { 248 SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, 249 spitz_set_jack), 250 SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, 251 spitz_set_spk), 252 }; 253 254 /* spitz digital audio interface glue - connects codec <--> CPU */ 255 SND_SOC_DAILINK_DEFS(wm8750, 256 DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")), 257 DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")), 258 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 259 260 static struct snd_soc_dai_link spitz_dai = { 261 .name = "wm8750", 262 .stream_name = "WM8750", 263 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 264 SND_SOC_DAIFMT_CBS_CFS, 265 .ops = &spitz_ops, 266 SND_SOC_DAILINK_REG(wm8750), 267 }; 268 269 /* spitz audio machine driver */ 270 static struct snd_soc_card snd_soc_spitz = { 271 .name = "Spitz", 272 .owner = THIS_MODULE, 273 .dai_link = &spitz_dai, 274 .num_links = 1, 275 276 .controls = wm8750_spitz_controls, 277 .num_controls = ARRAY_SIZE(wm8750_spitz_controls), 278 .dapm_widgets = wm8750_dapm_widgets, 279 .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), 280 .dapm_routes = spitz_audio_map, 281 .num_dapm_routes = ARRAY_SIZE(spitz_audio_map), 282 .fully_routed = true, 283 }; 284 285 static int spitz_probe(struct platform_device *pdev) 286 { 287 struct snd_soc_card *card = &snd_soc_spitz; 288 int ret; 289 290 if (machine_is_akita()) 291 spitz_mic_gpio = AKITA_GPIO_MIC_BIAS; 292 else 293 spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; 294 295 ret = gpio_request(spitz_mic_gpio, "MIC GPIO"); 296 if (ret) 297 goto err1; 298 299 ret = gpio_direction_output(spitz_mic_gpio, 0); 300 if (ret) 301 goto err2; 302 303 card->dev = &pdev->dev; 304 305 ret = devm_snd_soc_register_card(&pdev->dev, card); 306 if (ret) { 307 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 308 ret); 309 goto err2; 310 } 311 312 return 0; 313 314 err2: 315 gpio_free(spitz_mic_gpio); 316 err1: 317 return ret; 318 } 319 320 static int spitz_remove(struct platform_device *pdev) 321 { 322 gpio_free(spitz_mic_gpio); 323 return 0; 324 } 325 326 static struct platform_driver spitz_driver = { 327 .driver = { 328 .name = "spitz-audio", 329 .pm = &snd_soc_pm_ops, 330 }, 331 .probe = spitz_probe, 332 .remove = spitz_remove, 333 }; 334 335 module_platform_driver(spitz_driver); 336 337 MODULE_AUTHOR("Richard Purdie"); 338 MODULE_DESCRIPTION("ALSA SoC Spitz"); 339 MODULE_LICENSE("GPL"); 340 MODULE_ALIAS("platform:spitz-audio"); 341