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