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