1/* 2 * e740-wm9705.c -- SoC audio for e740 3 * 4 * Copyright 2007 (c) Ian Molton <spyro@f2s.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; version 2 ONLY. 9 * 10 */ 11 12#include <linux/module.h> 13#include <linux/moduleparam.h> 14#include <linux/gpio.h> 15 16#include <sound/core.h> 17#include <sound/pcm.h> 18#include <sound/soc.h> 19#include <sound/soc-dapm.h> 20 21#include <mach/audio.h> 22#include <mach/eseries-gpio.h> 23 24#include <asm/mach-types.h> 25 26#include "../codecs/wm9705.h" 27#include "pxa2xx-pcm.h" 28#include "pxa2xx-ac97.h" 29 30 31#define E740_AUDIO_OUT 1 32#define E740_AUDIO_IN 2 33 34static int e740_audio_power; 35 36static void e740_sync_audio_power(int status) 37{ 38 gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status); 39 gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0); 40 gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0); 41} 42 43static int e740_mic_amp_event(struct snd_soc_dapm_widget *w, 44 struct snd_kcontrol *kcontrol, int event) 45{ 46 if (event & SND_SOC_DAPM_PRE_PMU) 47 e740_audio_power |= E740_AUDIO_IN; 48 else if (event & SND_SOC_DAPM_POST_PMD) 49 e740_audio_power &= ~E740_AUDIO_IN; 50 51 e740_sync_audio_power(e740_audio_power); 52 53 return 0; 54} 55 56static int e740_output_amp_event(struct snd_soc_dapm_widget *w, 57 struct snd_kcontrol *kcontrol, int event) 58{ 59 if (event & SND_SOC_DAPM_PRE_PMU) 60 e740_audio_power |= E740_AUDIO_OUT; 61 else if (event & SND_SOC_DAPM_POST_PMD) 62 e740_audio_power &= ~E740_AUDIO_OUT; 63 64 e740_sync_audio_power(e740_audio_power); 65 66 return 0; 67} 68 69static const struct snd_soc_dapm_widget e740_dapm_widgets[] = { 70 SND_SOC_DAPM_HP("Headphone Jack", NULL), 71 SND_SOC_DAPM_SPK("Speaker", NULL), 72 SND_SOC_DAPM_MIC("Mic (Internal)", NULL), 73 SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0, 74 e740_output_amp_event, SND_SOC_DAPM_PRE_PMU | 75 SND_SOC_DAPM_POST_PMD), 76 SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0, 77 e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU | 78 SND_SOC_DAPM_POST_PMD), 79}; 80 81static const struct snd_soc_dapm_route audio_map[] = { 82 {"Output Amp", NULL, "LOUT"}, 83 {"Output Amp", NULL, "ROUT"}, 84 {"Output Amp", NULL, "MONOOUT"}, 85 86 {"Speaker", NULL, "Output Amp"}, 87 {"Headphone Jack", NULL, "Output Amp"}, 88 89 {"MIC1", NULL, "Mic Amp"}, 90 {"Mic Amp", NULL, "Mic (Internal)"}, 91}; 92 93static int e740_ac97_init(struct snd_soc_codec *codec) 94{ 95 snd_soc_dapm_nc_pin(codec, "HPOUTL"); 96 snd_soc_dapm_nc_pin(codec, "HPOUTR"); 97 snd_soc_dapm_nc_pin(codec, "PHONE"); 98 snd_soc_dapm_nc_pin(codec, "LINEINL"); 99 snd_soc_dapm_nc_pin(codec, "LINEINR"); 100 snd_soc_dapm_nc_pin(codec, "CDINL"); 101 snd_soc_dapm_nc_pin(codec, "CDINR"); 102 snd_soc_dapm_nc_pin(codec, "PCBEEP"); 103 snd_soc_dapm_nc_pin(codec, "MIC2"); 104 105 snd_soc_dapm_new_controls(codec, e740_dapm_widgets, 106 ARRAY_SIZE(e740_dapm_widgets)); 107 108 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); 109 110 snd_soc_dapm_sync(codec); 111 112 return 0; 113} 114 115static struct snd_soc_dai_link e740_dai[] = { 116 { 117 .name = "AC97", 118 .stream_name = "AC97 HiFi", 119 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], 120 .codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI], 121 .init = e740_ac97_init, 122 }, 123 { 124 .name = "AC97 Aux", 125 .stream_name = "AC97 Aux", 126 .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], 127 .codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX], 128 }, 129}; 130 131static struct snd_soc_card e740 = { 132 .name = "Toshiba e740", 133 .platform = &pxa2xx_soc_platform, 134 .dai_link = e740_dai, 135 .num_links = ARRAY_SIZE(e740_dai), 136}; 137 138static struct snd_soc_device e740_snd_devdata = { 139 .card = &e740, 140 .codec_dev = &soc_codec_dev_wm9705, 141}; 142 143static struct platform_device *e740_snd_device; 144 145static int __init e740_init(void) 146{ 147 int ret; 148 149 if (!machine_is_e740()) 150 return -ENODEV; 151 152 ret = gpio_request(GPIO_E740_MIC_ON, "Mic amp"); 153 if (ret) 154 return ret; 155 156 ret = gpio_request(GPIO_E740_AMP_ON, "Output amp"); 157 if (ret) 158 goto free_mic_amp_gpio; 159 160 ret = gpio_request(GPIO_E740_WM9705_nAVDD2, "Audio power"); 161 if (ret) 162 goto free_op_amp_gpio; 163 164 /* Disable audio */ 165 ret = gpio_direction_output(GPIO_E740_MIC_ON, 0); 166 if (ret) 167 goto free_apwr_gpio; 168 ret = gpio_direction_output(GPIO_E740_AMP_ON, 0); 169 if (ret) 170 goto free_apwr_gpio; 171 ret = gpio_direction_output(GPIO_E740_WM9705_nAVDD2, 1); 172 if (ret) 173 goto free_apwr_gpio; 174 175 e740_snd_device = platform_device_alloc("soc-audio", -1); 176 if (!e740_snd_device) { 177 ret = -ENOMEM; 178 goto free_apwr_gpio; 179 } 180 181 platform_set_drvdata(e740_snd_device, &e740_snd_devdata); 182 e740_snd_devdata.dev = &e740_snd_device->dev; 183 ret = platform_device_add(e740_snd_device); 184 185 if (!ret) 186 return 0; 187 188/* Fail gracefully */ 189 platform_device_put(e740_snd_device); 190free_apwr_gpio: 191 gpio_free(GPIO_E740_WM9705_nAVDD2); 192free_op_amp_gpio: 193 gpio_free(GPIO_E740_AMP_ON); 194free_mic_amp_gpio: 195 gpio_free(GPIO_E740_MIC_ON); 196 197 return ret; 198} 199 200static void __exit e740_exit(void) 201{ 202 platform_device_unregister(e740_snd_device); 203} 204 205module_init(e740_init); 206module_exit(e740_exit); 207 208/* Module information */ 209MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); 210MODULE_DESCRIPTION("ALSA SoC driver for e740"); 211MODULE_LICENSE("GPL v2"); 212