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/soc-dapm.h> 54#include <sound/initval.h> 55#include <sound/ac97_codec.h> 56 57#include "pxa2xx-pcm.h" 58#include "pxa2xx-ac97.h" 59#include "../codecs/wm9713.h" 60 61#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) 62 63#define AC97_GPIO_PULL 0x58 64 65/* Use GPIO8 for rear speaker amplifier */ 66static int rear_amp_power(struct snd_soc_codec *codec, int power) 67{ 68 unsigned short reg; 69 70 if (power) { 71 reg = snd_soc_read(codec, AC97_GPIO_CFG); 72 snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100); 73 reg = snd_soc_read(codec, AC97_GPIO_PULL); 74 snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15)); 75 } else { 76 reg = snd_soc_read(codec, AC97_GPIO_CFG); 77 snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100); 78 reg = snd_soc_read(codec, AC97_GPIO_PULL); 79 snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15)); 80 } 81 82 return 0; 83} 84 85static int rear_amp_event(struct snd_soc_dapm_widget *widget, 86 struct snd_kcontrol *kctl, int event) 87{ 88 struct snd_soc_codec *codec = widget->codec; 89 90 return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event)); 91} 92 93/* mioa701 machine dapm widgets */ 94static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = { 95 SND_SOC_DAPM_SPK("Front Speaker", NULL), 96 SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event), 97 SND_SOC_DAPM_MIC("Headset", NULL), 98 SND_SOC_DAPM_LINE("GSM Line Out", NULL), 99 SND_SOC_DAPM_LINE("GSM Line In", NULL), 100 SND_SOC_DAPM_MIC("Headset Mic", NULL), 101 SND_SOC_DAPM_MIC("Front Mic", NULL), 102}; 103 104static const struct snd_soc_dapm_route audio_map[] = { 105 /* Call Mic */ 106 {"Mic Bias", NULL, "Front Mic"}, 107 {"MIC1", NULL, "Mic Bias"}, 108 109 /* Headset Mic */ 110 {"LINEL", NULL, "Headset Mic"}, 111 {"LINER", NULL, "Headset Mic"}, 112 113 /* GSM Module */ 114 {"MONOIN", NULL, "GSM Line Out"}, 115 {"PCBEEP", NULL, "GSM Line Out"}, 116 {"GSM Line In", NULL, "MONO"}, 117 118 /* headphone connected to HPL, HPR */ 119 {"Headset", NULL, "HPL"}, 120 {"Headset", NULL, "HPR"}, 121 122 /* front speaker connected to HPL, OUT3 */ 123 {"Front Speaker", NULL, "HPL"}, 124 {"Front Speaker", NULL, "OUT3"}, 125 126 /* rear speaker connected to SPKL, SPKR */ 127 {"Rear Speaker", NULL, "SPKL"}, 128 {"Rear Speaker", NULL, "SPKR"}, 129}; 130 131static int mioa701_wm9713_init(struct snd_soc_codec *codec) 132{ 133 unsigned short reg; 134 135 /* Add mioa701 specific widgets */ 136 snd_soc_dapm_new_controls(codec, ARRAY_AND_SIZE(mioa701_dapm_widgets)); 137 138 /* Set up mioa701 specific audio path audio_mapnects */ 139 snd_soc_dapm_add_routes(codec, ARRAY_AND_SIZE(audio_map)); 140 141 /* Prepare GPIO8 for rear speaker amplifier */ 142 reg = codec->read(codec, AC97_GPIO_CFG); 143 codec->write(codec, AC97_GPIO_CFG, reg | 0x0100); 144 145 /* Prepare MIC input */ 146 reg = codec->read(codec, AC97_3D_CONTROL); 147 codec->write(codec, AC97_3D_CONTROL, reg | 0xc000); 148 149 snd_soc_dapm_enable_pin(codec, "Front Speaker"); 150 snd_soc_dapm_enable_pin(codec, "Rear Speaker"); 151 snd_soc_dapm_enable_pin(codec, "Front Mic"); 152 snd_soc_dapm_enable_pin(codec, "GSM Line In"); 153 snd_soc_dapm_enable_pin(codec, "GSM Line Out"); 154 snd_soc_dapm_sync(codec); 155 156 return 0; 157} 158 159static struct snd_soc_ops mioa701_ops; 160 161static struct snd_soc_dai_link mioa701_dai[] = { 162 { 163 .name = "AC97", 164 .stream_name = "AC97 HiFi", 165 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], 166 .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI], 167 .init = mioa701_wm9713_init, 168 .ops = &mioa701_ops, 169 }, 170 { 171 .name = "AC97 Aux", 172 .stream_name = "AC97 Aux", 173 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], 174 .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX], 175 .ops = &mioa701_ops, 176 }, 177}; 178 179static struct snd_soc_card mioa701 = { 180 .name = "MioA701", 181 .platform = &pxa2xx_soc_platform, 182 .dai_link = mioa701_dai, 183 .num_links = ARRAY_SIZE(mioa701_dai), 184}; 185 186static struct snd_soc_device mioa701_snd_devdata = { 187 .card = &mioa701, 188 .codec_dev = &soc_codec_dev_wm9713, 189}; 190 191static struct platform_device *mioa701_snd_device; 192 193static int mioa701_wm9713_probe(struct platform_device *pdev) 194{ 195 int ret; 196 197 if (!machine_is_mioa701()) 198 return -ENODEV; 199 200 dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" 201 "lead to overheating and possible destruction of your device." 202 "Do not use without a good knowledge of mio's board design!\n"); 203 204 mioa701_snd_device = platform_device_alloc("soc-audio", -1); 205 if (!mioa701_snd_device) 206 return -ENOMEM; 207 208 platform_set_drvdata(mioa701_snd_device, &mioa701_snd_devdata); 209 mioa701_snd_devdata.dev = &mioa701_snd_device->dev; 210 211 ret = platform_device_add(mioa701_snd_device); 212 if (!ret) 213 return 0; 214 215 platform_device_put(mioa701_snd_device); 216 return ret; 217} 218 219static int __devexit mioa701_wm9713_remove(struct platform_device *pdev) 220{ 221 platform_device_unregister(mioa701_snd_device); 222 return 0; 223} 224 225static struct platform_driver mioa701_wm9713_driver = { 226 .probe = mioa701_wm9713_probe, 227 .remove = __devexit_p(mioa701_wm9713_remove), 228 .driver = { 229 .name = "mioa701-wm9713", 230 .owner = THIS_MODULE, 231 }, 232}; 233 234static int __init mioa701_asoc_init(void) 235{ 236 return platform_driver_register(&mioa701_wm9713_driver); 237} 238 239static void __exit mioa701_asoc_exit(void) 240{ 241 platform_driver_unregister(&mioa701_wm9713_driver); 242} 243 244module_init(mioa701_asoc_init); 245module_exit(mioa701_asoc_exit); 246 247/* Module information */ 248MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)"); 249MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701"); 250MODULE_LICENSE("GPL"); 251