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/core.h> 26 #include <sound/pcm.h> 27 #include <sound/soc.h> 28 #include <sound/soc-dapm.h> 29 30 #include <asm/mach-types.h> 31 #include <asm/hardware/scoop.h> 32 #include <asm/arch/pxa-regs.h> 33 #include <asm/arch/hardware.h> 34 #include <asm/arch/akita.h> 35 #include <asm/arch/spitz.h> 36 #include "../codecs/wm8750.h" 37 #include "pxa2xx-pcm.h" 38 #include "pxa2xx-i2s.h" 39 40 #define SPITZ_HP 0 41 #define SPITZ_MIC 1 42 #define SPITZ_LINE 2 43 #define SPITZ_HEADSET 3 44 #define SPITZ_HP_OFF 4 45 #define SPITZ_SPK_ON 0 46 #define SPITZ_SPK_OFF 1 47 48 /* audio clock in Hz - rounded from 12.235MHz */ 49 #define SPITZ_AUDIO_CLOCK 12288000 50 51 static int spitz_jack_func; 52 static int spitz_spk_func; 53 54 static void spitz_ext_control(struct snd_soc_codec *codec) 55 { 56 if (spitz_spk_func == SPITZ_SPK_ON) 57 snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); 58 else 59 snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0); 60 61 /* set up jack connection */ 62 switch (spitz_jack_func) { 63 case SPITZ_HP: 64 /* enable and unmute hp jack, disable mic bias */ 65 snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); 66 snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); 67 snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); 68 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); 69 set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); 70 set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); 71 break; 72 case SPITZ_MIC: 73 /* enable mic jack and bias, mute hp */ 74 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); 75 snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); 76 snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); 77 snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); 78 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); 79 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); 80 break; 81 case SPITZ_LINE: 82 /* enable line jack, disable mic bias and mute hp */ 83 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); 84 snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); 85 snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); 86 snd_soc_dapm_set_endpoint(codec, "Line Jack", 1); 87 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); 88 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); 89 break; 90 case SPITZ_HEADSET: 91 /* enable and unmute headset jack enable mic bias, mute L hp */ 92 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); 93 snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); 94 snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); 95 snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1); 96 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); 97 set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); 98 break; 99 case SPITZ_HP_OFF: 100 101 /* jack removed, everything off */ 102 snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); 103 snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); 104 snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); 105 snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); 106 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); 107 reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); 108 break; 109 } 110 snd_soc_dapm_sync_endpoints(codec); 111 } 112 113 static int spitz_startup(struct snd_pcm_substream *substream) 114 { 115 struct snd_soc_pcm_runtime *rtd = substream->private_data; 116 struct snd_soc_codec *codec = rtd->socdev->codec; 117 118 /* check the jack status at stream startup */ 119 spitz_ext_control(codec); 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_codec_dai *codec_dai = rtd->dai->codec_dai; 128 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->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 = codec_dai->dai_ops.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 = cpu_dai->dai_ops.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 = codec_dai->dai_ops.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 = cpu_dai->dai_ops.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 if (machine_is_borzoi() || machine_is_spitz()) { 222 if (SND_SOC_DAPM_EVENT_ON(event)) 223 set_scoop_gpio(&spitzscoop2_device.dev, 224 SPITZ_SCP2_MIC_BIAS); 225 else 226 reset_scoop_gpio(&spitzscoop2_device.dev, 227 SPITZ_SCP2_MIC_BIAS); 228 } 229 230 if (machine_is_akita()) { 231 if (SND_SOC_DAPM_EVENT_ON(event)) 232 akita_set_ioexp(&akitaioexp_device.dev, 233 AKITA_IOEXP_MIC_BIAS); 234 else 235 akita_reset_ioexp(&akitaioexp_device.dev, 236 AKITA_IOEXP_MIC_BIAS); 237 } 238 return 0; 239 } 240 241 /* spitz machine dapm widgets */ 242 static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { 243 SND_SOC_DAPM_HP("Headphone Jack", NULL), 244 SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias), 245 SND_SOC_DAPM_SPK("Ext Spk", NULL), 246 SND_SOC_DAPM_LINE("Line Jack", NULL), 247 248 /* headset is a mic and mono headphone */ 249 SND_SOC_DAPM_HP("Headset Jack", NULL), 250 }; 251 252 /* Spitz machine audio_map */ 253 static const char *audio_map[][3] = { 254 255 /* headphone connected to LOUT1, ROUT1 */ 256 {"Headphone Jack", NULL, "LOUT1"}, 257 {"Headphone Jack", NULL, "ROUT1"}, 258 259 /* headset connected to ROUT1 and LINPUT1 with bias (def below) */ 260 {"Headset Jack", NULL, "ROUT1"}, 261 262 /* ext speaker connected to LOUT2, ROUT2 */ 263 {"Ext Spk", NULL , "ROUT2"}, 264 {"Ext Spk", NULL , "LOUT2"}, 265 266 /* mic is connected to input 1 - with bias */ 267 {"LINPUT1", NULL, "Mic Bias"}, 268 {"Mic Bias", NULL, "Mic Jack"}, 269 270 /* line is connected to input 1 - no bias */ 271 {"LINPUT1", NULL, "Line Jack"}, 272 273 {NULL, NULL, NULL}, 274 }; 275 276 static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset", 277 "Off"}; 278 static const char *spk_function[] = {"On", "Off"}; 279 static const struct soc_enum spitz_enum[] = { 280 SOC_ENUM_SINGLE_EXT(5, jack_function), 281 SOC_ENUM_SINGLE_EXT(2, spk_function), 282 }; 283 284 static const struct snd_kcontrol_new wm8750_spitz_controls[] = { 285 SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack, 286 spitz_set_jack), 287 SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk, 288 spitz_set_spk), 289 }; 290 291 /* 292 * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device 293 */ 294 static int spitz_wm8750_init(struct snd_soc_codec *codec) 295 { 296 int i, err; 297 298 /* NC codec pins */ 299 snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0); 300 snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0); 301 snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0); 302 snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0); 303 snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0); 304 snd_soc_dapm_set_endpoint(codec, "OUT3", 0); 305 snd_soc_dapm_set_endpoint(codec, "MONO", 0); 306 307 /* Add spitz specific controls */ 308 for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { 309 err = snd_ctl_add(codec->card, 310 snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); 311 if (err < 0) 312 return err; 313 } 314 315 /* Add spitz specific widgets */ 316 for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { 317 snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); 318 } 319 320 /* Set up spitz specific audio path audio_map */ 321 for (i = 0; audio_map[i][0] != NULL; i++) { 322 snd_soc_dapm_connect_input(codec, audio_map[i][0], 323 audio_map[i][1], audio_map[i][2]); 324 } 325 326 snd_soc_dapm_sync_endpoints(codec); 327 return 0; 328 } 329 330 /* spitz digital audio interface glue - connects codec <--> CPU */ 331 static struct snd_soc_dai_link spitz_dai = { 332 .name = "wm8750", 333 .stream_name = "WM8750", 334 .cpu_dai = &pxa_i2s_dai, 335 .codec_dai = &wm8750_dai, 336 .init = spitz_wm8750_init, 337 .ops = &spitz_ops, 338 }; 339 340 /* spitz audio machine driver */ 341 static struct snd_soc_machine snd_soc_machine_spitz = { 342 .name = "Spitz", 343 .dai_link = &spitz_dai, 344 .num_links = 1, 345 }; 346 347 /* spitz audio private data */ 348 static struct wm8750_setup_data spitz_wm8750_setup = { 349 .i2c_address = 0x1b, 350 }; 351 352 /* spitz audio subsystem */ 353 static struct snd_soc_device spitz_snd_devdata = { 354 .machine = &snd_soc_machine_spitz, 355 .platform = &pxa2xx_soc_platform, 356 .codec_dev = &soc_codec_dev_wm8750, 357 .codec_data = &spitz_wm8750_setup, 358 }; 359 360 static struct platform_device *spitz_snd_device; 361 362 static int __init spitz_init(void) 363 { 364 int ret; 365 366 if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita())) 367 return -ENODEV; 368 369 spitz_snd_device = platform_device_alloc("soc-audio", -1); 370 if (!spitz_snd_device) 371 return -ENOMEM; 372 373 platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata); 374 spitz_snd_devdata.dev = &spitz_snd_device->dev; 375 ret = platform_device_add(spitz_snd_device); 376 377 if (ret) 378 platform_device_put(spitz_snd_device); 379 380 return ret; 381 } 382 383 static void __exit spitz_exit(void) 384 { 385 platform_device_unregister(spitz_snd_device); 386 } 387 388 module_init(spitz_init); 389 module_exit(spitz_exit); 390 391 MODULE_AUTHOR("Richard Purdie"); 392 MODULE_DESCRIPTION("ALSA SoC Spitz"); 393 MODULE_LICENSE("GPL"); 394