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