1 /* 2 * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita 3 * 4 * Copyright 2005 Wolfson Microelectronics PLC. 5 * Copyright 2005 Openedhand Ltd. 6 * 7 * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 8 * Richard Purdie <richard@openedhand.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 */ 16 17 #include <linux/module.h> 18 #include <linux/moduleparam.h> 19 #include <linux/timer.h> 20 #include <linux/interrupt.h> 21 #include <linux/platform_device.h> 22 #include <linux/gpio.h> 23 #include <sound/core.h> 24 #include <sound/pcm.h> 25 #include <sound/soc.h> 26 27 #include <asm/mach-types.h> 28 #include <mach/spitz.h> 29 #include "../codecs/wm8750.h" 30 #include "pxa2xx-i2s.h" 31 32 #define SPITZ_HP 0 33 #define SPITZ_MIC 1 34 #define SPITZ_LINE 2 35 #define SPITZ_HEADSET 3 36 #define SPITZ_HP_OFF 4 37 #define SPITZ_SPK_ON 0 38 #define SPITZ_SPK_OFF 1 39 40 /* audio clock in Hz - rounded from 12.235MHz */ 41 #define SPITZ_AUDIO_CLOCK 12288000 42 43 static int spitz_jack_func; 44 static int spitz_spk_func; 45 static int spitz_mic_gpio; 46 47 static void spitz_ext_control(struct snd_soc_codec *codec) 48 { 49 struct snd_soc_dapm_context *dapm = &codec->dapm; 50 51 if (spitz_spk_func == SPITZ_SPK_ON) 52 snd_soc_dapm_enable_pin(dapm, "Ext Spk"); 53 else 54 snd_soc_dapm_disable_pin(dapm, "Ext Spk"); 55 56 /* set up jack connection */ 57 switch (spitz_jack_func) { 58 case SPITZ_HP: 59 /* enable and unmute hp jack, disable mic bias */ 60 snd_soc_dapm_disable_pin(dapm, "Headset Jack"); 61 snd_soc_dapm_disable_pin(dapm, "Mic Jack"); 62 snd_soc_dapm_disable_pin(dapm, "Line Jack"); 63 snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); 64 gpio_set_value(SPITZ_GPIO_MUTE_L, 1); 65 gpio_set_value(SPITZ_GPIO_MUTE_R, 1); 66 break; 67 case SPITZ_MIC: 68 /* enable mic jack and bias, mute hp */ 69 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 70 snd_soc_dapm_disable_pin(dapm, "Headset Jack"); 71 snd_soc_dapm_disable_pin(dapm, "Line Jack"); 72 snd_soc_dapm_enable_pin(dapm, "Mic Jack"); 73 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 74 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 75 break; 76 case SPITZ_LINE: 77 /* enable line jack, disable mic bias and mute hp */ 78 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 79 snd_soc_dapm_disable_pin(dapm, "Headset Jack"); 80 snd_soc_dapm_disable_pin(dapm, "Mic Jack"); 81 snd_soc_dapm_enable_pin(dapm, "Line Jack"); 82 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 83 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 84 break; 85 case SPITZ_HEADSET: 86 /* enable and unmute headset jack enable mic bias, mute L hp */ 87 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 88 snd_soc_dapm_enable_pin(dapm, "Mic Jack"); 89 snd_soc_dapm_disable_pin(dapm, "Line Jack"); 90 snd_soc_dapm_enable_pin(dapm, "Headset Jack"); 91 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 92 gpio_set_value(SPITZ_GPIO_MUTE_R, 1); 93 break; 94 case SPITZ_HP_OFF: 95 96 /* jack removed, everything off */ 97 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 98 snd_soc_dapm_disable_pin(dapm, "Headset Jack"); 99 snd_soc_dapm_disable_pin(dapm, "Mic Jack"); 100 snd_soc_dapm_disable_pin(dapm, "Line Jack"); 101 gpio_set_value(SPITZ_GPIO_MUTE_L, 0); 102 gpio_set_value(SPITZ_GPIO_MUTE_R, 0); 103 break; 104 } 105 snd_soc_dapm_sync(dapm); 106 } 107 108 static int spitz_startup(struct snd_pcm_substream *substream) 109 { 110 struct snd_soc_pcm_runtime *rtd = substream->private_data; 111 struct snd_soc_codec *codec = rtd->codec; 112 113 mutex_lock(&codec->mutex); 114 115 /* check the jack status at stream startup */ 116 spitz_ext_control(codec); 117 118 mutex_unlock(&codec->mutex); 119 120 return 0; 121 } 122 123 static int spitz_hw_params(struct snd_pcm_substream *substream, 124 struct snd_pcm_hw_params *params) 125 { 126 struct snd_soc_pcm_runtime *rtd = substream->private_data; 127 struct snd_soc_dai *codec_dai = rtd->codec_dai; 128 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 129 unsigned int clk = 0; 130 int ret = 0; 131 132 switch (params_rate(params)) { 133 case 8000: 134 case 16000: 135 case 48000: 136 case 96000: 137 clk = 12288000; 138 break; 139 case 11025: 140 case 22050: 141 case 44100: 142 clk = 11289600; 143 break; 144 } 145 146 /* set codec DAI configuration */ 147 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | 148 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 149 if (ret < 0) 150 return ret; 151 152 /* set cpu DAI configuration */ 153 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | 154 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 155 if (ret < 0) 156 return ret; 157 158 /* set the codec system clock for DAC and ADC */ 159 ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk, 160 SND_SOC_CLOCK_IN); 161 if (ret < 0) 162 return ret; 163 164 /* set the I2S system clock as input (unused) */ 165 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 166 SND_SOC_CLOCK_IN); 167 if (ret < 0) 168 return ret; 169 170 return 0; 171 } 172 173 static struct snd_soc_ops spitz_ops = { 174 .startup = spitz_startup, 175 .hw_params = spitz_hw_params, 176 }; 177 178 static int spitz_get_jack(struct snd_kcontrol *kcontrol, 179 struct snd_ctl_elem_value *ucontrol) 180 { 181 ucontrol->value.integer.value[0] = spitz_jack_func; 182 return 0; 183 } 184 185 static int spitz_set_jack(struct snd_kcontrol *kcontrol, 186 struct snd_ctl_elem_value *ucontrol) 187 { 188 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 189 190 if (spitz_jack_func == ucontrol->value.integer.value[0]) 191 return 0; 192 193 spitz_jack_func = ucontrol->value.integer.value[0]; 194 spitz_ext_control(codec); 195 return 1; 196 } 197 198 static int spitz_get_spk(struct snd_kcontrol *kcontrol, 199 struct snd_ctl_elem_value *ucontrol) 200 { 201 ucontrol->value.integer.value[0] = spitz_spk_func; 202 return 0; 203 } 204 205 static int spitz_set_spk(struct snd_kcontrol *kcontrol, 206 struct snd_ctl_elem_value *ucontrol) 207 { 208 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 209 210 if (spitz_spk_func == ucontrol->value.integer.value[0]) 211 return 0; 212 213 spitz_spk_func = ucontrol->value.integer.value[0]; 214 spitz_ext_control(codec); 215 return 1; 216 } 217 218 static int spitz_mic_bias(struct snd_soc_dapm_widget *w, 219 struct snd_kcontrol *k, int event) 220 { 221 gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event)); 222 return 0; 223 } 224 225 /* spitz machine dapm widgets */ 226 static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 227 SND_SOC_DAPM_HP("Headphone Jack", NULL), 228 SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), 229 SND_SOC_DAPM_SPK("Ext Spk", NULL), 230 SND_SOC_DAPM_LINE("Line Jack", NULL), 231 232 /* headset is a mic and mono headphone */ 233 SND_SOC_DAPM_HP("Headset Jack", NULL), 234 }; 235 236 /* Spitz machine audio_map */ 237 static const struct snd_soc_dapm_route audio_map[] = { 238 239 /* headphone connected to LOUT1, ROUT1 */ 240 {"Headphone Jack", NULL, "LOUT1"}, 241 {"Headphone Jack", NULL, "ROUT1"}, 242 243 /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ 244 {"Headset Jack", NULL, "ROUT1"}, 245 246 /* ext speaker connected to LOUT2, ROUT2 */ 247 {"Ext Spk", NULL , "ROUT2"}, 248 {"Ext Spk", NULL , "LOUT2"}, 249 250 /* mic is connected to input 1 - with bias */ 251 {"LINPUT1", NULL, "Mic Bias"}, 252 {"Mic Bias", NULL, "Mic Jack"}, 253 254 /* line is connected to input 1 - no bias */ 255 {"LINPUT1", NULL, "Line Jack"}, 256 }; 257 258 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", 259 "Off"}; 260 static const char *spk_function[] = {"On", "Off"}; 261 static const struct soc_enum spitz_enum[] = { 262 SOC_ENUM_SINGLE_EXT(5, jack_function), 263 SOC_ENUM_SINGLE_EXT(2, spk_function), 264 }; 265 266 static const struct snd_kcontrol_new wm8750_spitz_controls[] = { 267 SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, 268 spitz_set_jack), 269 SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, 270 spitz_set_spk), 271 }; 272 273 /* 274 * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device 275 */ 276 static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) 277 { 278 struct snd_soc_codec *codec = rtd->codec; 279 struct snd_soc_dapm_context *dapm = &codec->dapm; 280 int err; 281 282 /* NC codec pins */ 283 snd_soc_dapm_nc_pin(dapm, "RINPUT1"); 284 snd_soc_dapm_nc_pin(dapm, "LINPUT2"); 285 snd_soc_dapm_nc_pin(dapm, "RINPUT2"); 286 snd_soc_dapm_nc_pin(dapm, "LINPUT3"); 287 snd_soc_dapm_nc_pin(dapm, "RINPUT3"); 288 snd_soc_dapm_nc_pin(dapm, "OUT3"); 289 snd_soc_dapm_nc_pin(dapm, "MONO1"); 290 291 /* Add spitz specific controls */ 292 err = snd_soc_add_controls(codec, wm8750_spitz_controls, 293 ARRAY_SIZE(wm8750_spitz_controls)); 294 if (err < 0) 295 return err; 296 297 /* Add spitz specific widgets */ 298 snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, 299 ARRAY_SIZE(wm8750_dapm_widgets)); 300 301 /* Set up spitz specific audio paths */ 302 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); 303 304 return 0; 305 } 306 307 /* spitz digital audio interface glue - connects codec <--> CPU */ 308 static struct snd_soc_dai_link spitz_dai = { 309 .name = "wm8750", 310 .stream_name = "WM8750", 311 .cpu_dai_name = "pxa2xx-i2s", 312 .codec_dai_name = "wm8750-hifi", 313 .platform_name = "pxa-pcm-audio", 314 .codec_name = "wm8750.0-001b", 315 .init = spitz_wm8750_init, 316 .ops = &spitz_ops, 317 }; 318 319 /* spitz audio machine driver */ 320 static struct snd_soc_card snd_soc_spitz = { 321 .name = "Spitz", 322 .dai_link = &spitz_dai, 323 .num_links = 1, 324 }; 325 326 static struct platform_device *spitz_snd_device; 327 328 static int __init spitz_init(void) 329 { 330 int ret; 331 332 if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) 333 return -ENODEV; 334 335 if (machine_is_borzoi() || machine_is_spitz()) 336 spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS; 337 else 338 spitz_mic_gpio = AKITA_GPIO_MIC_BIAS; 339 340 ret = gpio_request(spitz_mic_gpio, "MIC GPIO"); 341 if (ret) 342 goto err1; 343 344 ret = gpio_direction_output(spitz_mic_gpio, 0); 345 if (ret) 346 goto err2; 347 348 spitz_snd_device = platform_device_alloc("soc-audio", -1); 349 if (!spitz_snd_device) { 350 ret = -ENOMEM; 351 goto err2; 352 } 353 354 platform_set_drvdata(spitz_snd_device, &snd_soc_spitz); 355 356 ret = platform_device_add(spitz_snd_device); 357 if (ret) 358 goto err3; 359 360 return 0; 361 362 err3: 363 platform_device_put(spitz_snd_device); 364 err2: 365 gpio_free(spitz_mic_gpio); 366 err1: 367 return ret; 368 } 369 370 static void __exit spitz_exit(void) 371 { 372 platform_device_unregister(spitz_snd_device); 373 gpio_free(spitz_mic_gpio); 374 } 375 376 module_init(spitz_init); 377 module_exit(spitz_exit); 378 379 MODULE_AUTHOR("Richard Purdie"); 380 MODULE_DESCRIPTION("ALSA SoC Spitz"); 381 MODULE_LICENSE("GPL"); 382