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