1/* 2 * poodle.c -- SoC audio for Poodle 3 * 4 * Copyright 2005 Wolfson Microelectronics PLC. 5 * Copyright 2005 Openedhand Ltd. 6 * 7 * Authors: Liam Girdwood <lrg@slimlogic.co.uk> 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 */ 16 17#include <linux/module.h> 18#include <linux/moduleparam.h> 19#include <linux/timer.h> 20#include <linux/i2c.h> 21#include <linux/interrupt.h> 22#include <linux/platform_device.h> 23#include <sound/core.h> 24#include <sound/pcm.h> 25#include <sound/soc.h> 26 27#include <asm/mach-types.h> 28#include <asm/hardware/locomo.h> 29#include <mach/poodle.h> 30#include <mach/audio.h> 31 32#include "../codecs/wm8731.h" 33#include "pxa2xx-i2s.h" 34 35#define POODLE_HP 1 36#define POODLE_HP_OFF 0 37#define POODLE_SPK_ON 1 38#define POODLE_SPK_OFF 0 39 40 /* audio clock in Hz - rounded from 12.235MHz */ 41#define POODLE_AUDIO_CLOCK 12288000 42 43static int poodle_jack_func; 44static int poodle_spk_func; 45 46static void poodle_ext_control(struct snd_soc_dapm_context *dapm) 47{ 48 /* set up jack connection */ 49 if (poodle_jack_func == POODLE_HP) { 50 /* set = unmute headphone */ 51 locomo_gpio_write(&poodle_locomo_device.dev, 52 POODLE_LOCOMO_GPIO_MUTE_L, 1); 53 locomo_gpio_write(&poodle_locomo_device.dev, 54 POODLE_LOCOMO_GPIO_MUTE_R, 1); 55 snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); 56 } else { 57 locomo_gpio_write(&poodle_locomo_device.dev, 58 POODLE_LOCOMO_GPIO_MUTE_L, 0); 59 locomo_gpio_write(&poodle_locomo_device.dev, 60 POODLE_LOCOMO_GPIO_MUTE_R, 0); 61 snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); 62 } 63 64 /* set the enpoints to their new connetion states */ 65 if (poodle_spk_func == POODLE_SPK_ON) 66 snd_soc_dapm_enable_pin(dapm, "Ext Spk"); 67 else 68 snd_soc_dapm_disable_pin(dapm, "Ext Spk"); 69 70 /* signal a DAPM event */ 71 snd_soc_dapm_sync(dapm); 72} 73 74static int poodle_startup(struct snd_pcm_substream *substream) 75{ 76 struct snd_soc_pcm_runtime *rtd = substream->private_data; 77 78 /* check the jack status at stream startup */ 79 poodle_ext_control(&rtd->card->dapm); 80 81 return 0; 82} 83 84/* we need to unmute the HP at shutdown as the mute burns power on poodle */ 85static void poodle_shutdown(struct snd_pcm_substream *substream) 86{ 87 /* set = unmute headphone */ 88 locomo_gpio_write(&poodle_locomo_device.dev, 89 POODLE_LOCOMO_GPIO_MUTE_L, 1); 90 locomo_gpio_write(&poodle_locomo_device.dev, 91 POODLE_LOCOMO_GPIO_MUTE_R, 1); 92} 93 94static int poodle_hw_params(struct snd_pcm_substream *substream, 95 struct snd_pcm_hw_params *params) 96{ 97 struct snd_soc_pcm_runtime *rtd = substream->private_data; 98 struct snd_soc_dai *codec_dai = rtd->codec_dai; 99 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 100 unsigned int clk = 0; 101 int ret = 0; 102 103 switch (params_rate(params)) { 104 case 8000: 105 case 16000: 106 case 48000: 107 case 96000: 108 clk = 12288000; 109 break; 110 case 11025: 111 case 22050: 112 case 44100: 113 clk = 11289600; 114 break; 115 } 116 117 /* set the codec system clock for DAC and ADC */ 118 ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk, 119 SND_SOC_CLOCK_IN); 120 if (ret < 0) 121 return ret; 122 123 /* set the I2S system clock as input (unused) */ 124 ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0, 125 SND_SOC_CLOCK_IN); 126 if (ret < 0) 127 return ret; 128 129 return 0; 130} 131 132static struct snd_soc_ops poodle_ops = { 133 .startup = poodle_startup, 134 .hw_params = poodle_hw_params, 135 .shutdown = poodle_shutdown, 136}; 137 138static int poodle_get_jack(struct snd_kcontrol *kcontrol, 139 struct snd_ctl_elem_value *ucontrol) 140{ 141 ucontrol->value.integer.value[0] = poodle_jack_func; 142 return 0; 143} 144 145static int poodle_set_jack(struct snd_kcontrol *kcontrol, 146 struct snd_ctl_elem_value *ucontrol) 147{ 148 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 149 150 if (poodle_jack_func == ucontrol->value.integer.value[0]) 151 return 0; 152 153 poodle_jack_func = ucontrol->value.integer.value[0]; 154 poodle_ext_control(&card->dapm); 155 return 1; 156} 157 158static int poodle_get_spk(struct snd_kcontrol *kcontrol, 159 struct snd_ctl_elem_value *ucontrol) 160{ 161 ucontrol->value.integer.value[0] = poodle_spk_func; 162 return 0; 163} 164 165static int poodle_set_spk(struct snd_kcontrol *kcontrol, 166 struct snd_ctl_elem_value *ucontrol) 167{ 168 struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); 169 170 if (poodle_spk_func == ucontrol->value.integer.value[0]) 171 return 0; 172 173 poodle_spk_func = ucontrol->value.integer.value[0]; 174 poodle_ext_control(&card->dapm); 175 return 1; 176} 177 178static int poodle_amp_event(struct snd_soc_dapm_widget *w, 179 struct snd_kcontrol *k, int event) 180{ 181 if (SND_SOC_DAPM_EVENT_ON(event)) 182 locomo_gpio_write(&poodle_locomo_device.dev, 183 POODLE_LOCOMO_GPIO_AMP_ON, 0); 184 else 185 locomo_gpio_write(&poodle_locomo_device.dev, 186 POODLE_LOCOMO_GPIO_AMP_ON, 1); 187 188 return 0; 189} 190 191/* poodle machine dapm widgets */ 192static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { 193SND_SOC_DAPM_HP("Headphone Jack", NULL), 194SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), 195}; 196 197/* Corgi machine connections to the codec pins */ 198static const struct snd_soc_dapm_route poodle_audio_map[] = { 199 200 /* headphone connected to LHPOUT1, RHPOUT1 */ 201 {"Headphone Jack", NULL, "LHPOUT"}, 202 {"Headphone Jack", NULL, "RHPOUT"}, 203 204 /* speaker connected to LOUT, ROUT */ 205 {"Ext Spk", NULL, "ROUT"}, 206 {"Ext Spk", NULL, "LOUT"}, 207}; 208 209static const char *jack_function[] = {"Off", "Headphone"}; 210static const char *spk_function[] = {"Off", "On"}; 211static const struct soc_enum poodle_enum[] = { 212 SOC_ENUM_SINGLE_EXT(2, jack_function), 213 SOC_ENUM_SINGLE_EXT(2, spk_function), 214}; 215 216static const struct snd_kcontrol_new wm8731_poodle_controls[] = { 217 SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack, 218 poodle_set_jack), 219 SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk, 220 poodle_set_spk), 221}; 222 223/* 224 * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device 225 */ 226static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) 227{ 228 struct snd_soc_codec *codec = rtd->codec; 229 struct snd_soc_dapm_context *dapm = &codec->dapm; 230 231 snd_soc_dapm_nc_pin(dapm, "LLINEIN"); 232 snd_soc_dapm_nc_pin(dapm, "RLINEIN"); 233 234 return 0; 235} 236 237/* poodle digital audio interface glue - connects codec <--> CPU */ 238static struct snd_soc_dai_link poodle_dai = { 239 .name = "WM8731", 240 .stream_name = "WM8731", 241 .cpu_dai_name = "pxa2xx-i2s", 242 .codec_dai_name = "wm8731-hifi", 243 .platform_name = "pxa-pcm-audio", 244 .codec_name = "wm8731.0-001b", 245 .init = poodle_wm8731_init, 246 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 247 SND_SOC_DAIFMT_CBS_CFS, 248 .ops = &poodle_ops, 249}; 250 251/* poodle audio machine driver */ 252static struct snd_soc_card poodle = { 253 .name = "Poodle", 254 .dai_link = &poodle_dai, 255 .num_links = 1, 256 .owner = THIS_MODULE, 257 258 .controls = wm8731_poodle_controls, 259 .num_controls = ARRAY_SIZE(wm8731_poodle_controls), 260 .dapm_widgets = wm8731_dapm_widgets, 261 .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), 262 .dapm_routes = poodle_audio_map, 263 .num_dapm_routes = ARRAY_SIZE(poodle_audio_map), 264}; 265 266static int poodle_probe(struct platform_device *pdev) 267{ 268 struct snd_soc_card *card = &poodle; 269 int ret; 270 271 locomo_gpio_set_dir(&poodle_locomo_device.dev, 272 POODLE_LOCOMO_GPIO_AMP_ON, 0); 273 /* should we mute HP at startup - burning power ?*/ 274 locomo_gpio_set_dir(&poodle_locomo_device.dev, 275 POODLE_LOCOMO_GPIO_MUTE_L, 0); 276 locomo_gpio_set_dir(&poodle_locomo_device.dev, 277 POODLE_LOCOMO_GPIO_MUTE_R, 0); 278 279 card->dev = &pdev->dev; 280 281 ret = snd_soc_register_card(card); 282 if (ret) 283 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 284 ret); 285 return ret; 286} 287 288static int poodle_remove(struct platform_device *pdev) 289{ 290 struct snd_soc_card *card = platform_get_drvdata(pdev); 291 292 snd_soc_unregister_card(card); 293 return 0; 294} 295 296static struct platform_driver poodle_driver = { 297 .driver = { 298 .name = "poodle-audio", 299 .pm = &snd_soc_pm_ops, 300 }, 301 .probe = poodle_probe, 302 .remove = poodle_remove, 303}; 304 305module_platform_driver(poodle_driver); 306 307/* Module information */ 308MODULE_AUTHOR("Richard Purdie"); 309MODULE_DESCRIPTION("ALSA SoC Poodle"); 310MODULE_LICENSE("GPL"); 311MODULE_ALIAS("platform:poodle-audio"); 312