1 /* 2 * Handles the Mitac mioa701 SoC system 3 * 4 * Copyright (C) 2008 Robert Jarzmik 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation in version 2 of the License. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 * This is a little schema of the sound interconnections : 20 * 21 * Sagem X200 Wolfson WM9713 22 * +--------+ +-------------------+ Rear Speaker 23 * | | | | /-+ 24 * | +--->----->---+MONOIN SPKL+--->----+-+ | 25 * | GSM | | | | | | 26 * | +--->----->---+PCBEEP SPKR+--->----+-+ | 27 * | CHIP | | | \-+ 28 * | +---<-----<---+MONO | 29 * | | | | Front Speaker 30 * +--------+ | | /-+ 31 * | HPL+--->----+-+ | 32 * | | | | | 33 * | OUT3+--->----+-+ | 34 * | | \-+ 35 * | | 36 * | | Front Micro 37 * | | + 38 * | MIC1+-----<--+o+ 39 * | | + 40 * +-------------------+ --- 41 */ 42 43 #include <linux/module.h> 44 #include <linux/moduleparam.h> 45 #include <linux/platform_device.h> 46 47 #include <asm/mach-types.h> 48 #include <mach/audio.h> 49 50 #include <sound/core.h> 51 #include <sound/pcm.h> 52 #include <sound/soc.h> 53 #include <sound/initval.h> 54 #include <sound/ac97_codec.h> 55 56 #include "pxa2xx-ac97.h" 57 #include "../codecs/wm9713.h" 58 59 #define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) 60 61 #define AC97_GPIO_PULL 0x58 62 63 /* Use GPIO8 for rear speaker amplifier */ 64 static int rear_amp_power(struct snd_soc_codec *codec, int power) 65 { 66 unsigned short reg; 67 68 if (power) { 69 reg = snd_soc_read(codec, AC97_GPIO_CFG); 70 snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100); 71 reg = snd_soc_read(codec, AC97_GPIO_PULL); 72 snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15)); 73 } else { 74 reg = snd_soc_read(codec, AC97_GPIO_CFG); 75 snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100); 76 reg = snd_soc_read(codec, AC97_GPIO_PULL); 77 snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15)); 78 } 79 80 return 0; 81 } 82 83 static int rear_amp_event(struct snd_soc_dapm_widget *widget, 84 struct snd_kcontrol *kctl, int event) 85 { 86 struct snd_soc_codec *codec = widget->codec; 87 88 return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event)); 89 } 90 91 /* mioa701 machine dapm widgets */ 92 static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = { 93 SND_SOC_DAPM_SPK("Front Speaker", NULL), 94 SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event), 95 SND_SOC_DAPM_MIC("Headset", NULL), 96 SND_SOC_DAPM_LINE("GSM Line Out", NULL), 97 SND_SOC_DAPM_LINE("GSM Line In", NULL), 98 SND_SOC_DAPM_MIC("Headset Mic", NULL), 99 SND_SOC_DAPM_MIC("Front Mic", NULL), 100 }; 101 102 static const struct snd_soc_dapm_route audio_map[] = { 103 /* Call Mic */ 104 {"Mic Bias", NULL, "Front Mic"}, 105 {"MIC1", NULL, "Mic Bias"}, 106 107 /* Headset Mic */ 108 {"LINEL", NULL, "Headset Mic"}, 109 {"LINER", NULL, "Headset Mic"}, 110 111 /* GSM Module */ 112 {"MONOIN", NULL, "GSM Line Out"}, 113 {"PCBEEP", NULL, "GSM Line Out"}, 114 {"GSM Line In", NULL, "MONO"}, 115 116 /* headphone connected to HPL, HPR */ 117 {"Headset", NULL, "HPL"}, 118 {"Headset", NULL, "HPR"}, 119 120 /* front speaker connected to HPL, OUT3 */ 121 {"Front Speaker", NULL, "HPL"}, 122 {"Front Speaker", NULL, "OUT3"}, 123 124 /* rear speaker connected to SPKL, SPKR */ 125 {"Rear Speaker", NULL, "SPKL"}, 126 {"Rear Speaker", NULL, "SPKR"}, 127 }; 128 129 static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) 130 { 131 struct snd_soc_codec *codec = rtd->codec; 132 struct snd_soc_dapm_context *dapm = &codec->dapm; 133 unsigned short reg; 134 135 /* Add mioa701 specific widgets */ 136 snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets)); 137 138 /* Set up mioa701 specific audio path audio_mapnects */ 139 snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map)); 140 141 /* Prepare GPIO8 for rear speaker amplifier */ 142 reg = codec->driver->read(codec, AC97_GPIO_CFG); 143 codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100); 144 145 /* Prepare MIC input */ 146 reg = codec->driver->read(codec, AC97_3D_CONTROL); 147 codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000); 148 149 snd_soc_dapm_enable_pin(dapm, "Front Speaker"); 150 snd_soc_dapm_enable_pin(dapm, "Rear Speaker"); 151 snd_soc_dapm_enable_pin(dapm, "Front Mic"); 152 snd_soc_dapm_enable_pin(dapm, "GSM Line In"); 153 snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); 154 snd_soc_dapm_sync(dapm); 155 156 return 0; 157 } 158 159 static struct snd_soc_ops mioa701_ops; 160 161 static struct snd_soc_dai_link mioa701_dai[] = { 162 { 163 .name = "AC97", 164 .stream_name = "AC97 HiFi", 165 .cpu_dai_name = "pxa2xx-ac97", 166 .codec_dai_name = "wm9713-hifi", 167 .codec_name = "wm9713-codec", 168 .init = mioa701_wm9713_init, 169 .platform_name = "pxa-pcm-audio", 170 .ops = &mioa701_ops, 171 }, 172 { 173 .name = "AC97 Aux", 174 .stream_name = "AC97 Aux", 175 .cpu_dai_name = "pxa2xx-ac97-aux", 176 .codec_dai_name ="wm9713-aux", 177 .codec_name = "wm9713-codec", 178 .platform_name = "pxa-pcm-audio", 179 .ops = &mioa701_ops, 180 }, 181 }; 182 183 static struct snd_soc_card mioa701 = { 184 .name = "MioA701", 185 .dai_link = mioa701_dai, 186 .num_links = ARRAY_SIZE(mioa701_dai), 187 }; 188 189 static struct platform_device *mioa701_snd_device; 190 191 static int mioa701_wm9713_probe(struct platform_device *pdev) 192 { 193 int ret; 194 195 if (!machine_is_mioa701()) 196 return -ENODEV; 197 198 dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" 199 "lead to overheating and possible destruction of your device." 200 "Do not use without a good knowledge of mio's board design!\n"); 201 202 mioa701_snd_device = platform_device_alloc("soc-audio", -1); 203 if (!mioa701_snd_device) 204 return -ENOMEM; 205 206 platform_set_drvdata(mioa701_snd_device, &mioa701); 207 208 ret = platform_device_add(mioa701_snd_device); 209 if (!ret) 210 return 0; 211 212 platform_device_put(mioa701_snd_device); 213 return ret; 214 } 215 216 static int __devexit mioa701_wm9713_remove(struct platform_device *pdev) 217 { 218 platform_device_unregister(mioa701_snd_device); 219 return 0; 220 } 221 222 static struct platform_driver mioa701_wm9713_driver = { 223 .probe = mioa701_wm9713_probe, 224 .remove = __devexit_p(mioa701_wm9713_remove), 225 .driver = { 226 .name = "mioa701-wm9713", 227 .owner = THIS_MODULE, 228 }, 229 }; 230 231 static int __init mioa701_asoc_init(void) 232 { 233 return platform_driver_register(&mioa701_wm9713_driver); 234 } 235 236 static void __exit mioa701_asoc_exit(void) 237 { 238 platform_driver_unregister(&mioa701_wm9713_driver); 239 } 240 241 module_init(mioa701_asoc_init); 242 module_exit(mioa701_asoc_exit); 243 244 /* Module information */ 245 MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)"); 246 MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701"); 247 MODULE_LICENSE("GPL"); 248