Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.2 324 lines 9.0 kB view raw
1/* 2 * omap3pandora.c -- SoC audio for Pandora Handheld Console 3 * 4 * Author: Gražvydas Ignotas <notasas@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * version 2 as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * 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., 51 Franklin St, Fifth Floor, Boston, MA 18 * 02110-1301 USA 19 * 20 */ 21 22#include <linux/clk.h> 23#include <linux/platform_device.h> 24#include <linux/gpio.h> 25#include <linux/delay.h> 26#include <linux/regulator/consumer.h> 27#include <linux/module.h> 28 29#include <sound/core.h> 30#include <sound/pcm.h> 31#include <sound/soc.h> 32 33#include <asm/mach-types.h> 34#include <plat/mcbsp.h> 35 36#include "omap-mcbsp.h" 37#include "omap-pcm.h" 38 39#define OMAP3_PANDORA_DAC_POWER_GPIO 118 40#define OMAP3_PANDORA_AMP_POWER_GPIO 14 41 42#define PREFIX "ASoC omap3pandora: " 43 44static struct regulator *omap3pandora_dac_reg; 45 46static int omap3pandora_hw_params(struct snd_pcm_substream *substream, 47 struct snd_pcm_hw_params *params) 48{ 49 struct snd_soc_pcm_runtime *rtd = substream->private_data; 50 struct snd_soc_dai *codec_dai = rtd->codec_dai; 51 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 52 int ret; 53 54 /* Set the codec system clock for DAC and ADC */ 55 ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, 56 SND_SOC_CLOCK_IN); 57 if (ret < 0) { 58 pr_err(PREFIX "can't set codec system clock\n"); 59 return ret; 60 } 61 62 /* Set McBSP clock to external */ 63 ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 64 256 * params_rate(params), 65 SND_SOC_CLOCK_IN); 66 if (ret < 0) { 67 pr_err(PREFIX "can't set cpu system clock\n"); 68 return ret; 69 } 70 71 ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8); 72 if (ret < 0) { 73 pr_err(PREFIX "can't set SRG clock divider\n"); 74 return ret; 75 } 76 77 return 0; 78} 79 80static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, 81 struct snd_kcontrol *k, int event) 82{ 83 /* 84 * The PCM1773 DAC datasheet requires 1ms delay between switching 85 * VCC power on/off and /PD pin high/low 86 */ 87 if (SND_SOC_DAPM_EVENT_ON(event)) { 88 regulator_enable(omap3pandora_dac_reg); 89 mdelay(1); 90 gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); 91 } else { 92 gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); 93 mdelay(1); 94 regulator_disable(omap3pandora_dac_reg); 95 } 96 97 return 0; 98} 99 100static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, 101 struct snd_kcontrol *k, int event) 102{ 103 if (SND_SOC_DAPM_EVENT_ON(event)) 104 gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); 105 else 106 gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); 107 108 return 0; 109} 110 111/* 112 * Audio paths on Pandora board: 113 * 114 * |O| ---> PCM DAC +-> AMP -> Headphone Jack 115 * |M| A +--------> Line Out 116 * |A| <~~clk~~+ 117 * |P| <--- TWL4030 <--------- Line In and MICs 118 */ 119static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = { 120 SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM, 121 0, 0, omap3pandora_dac_event, 122 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 123 SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, 124 0, 0, NULL, 0, omap3pandora_hp_event, 125 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 126 SND_SOC_DAPM_HP("Headphone Jack", NULL), 127 SND_SOC_DAPM_LINE("Line Out", NULL), 128}; 129 130static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = { 131 SND_SOC_DAPM_MIC("Mic (internal)", NULL), 132 SND_SOC_DAPM_MIC("Mic (external)", NULL), 133 SND_SOC_DAPM_LINE("Line In", NULL), 134}; 135 136static const struct snd_soc_dapm_route omap3pandora_out_map[] = { 137 {"PCM DAC", NULL, "APLL Enable"}, 138 {"Headphone Amplifier", NULL, "PCM DAC"}, 139 {"Line Out", NULL, "PCM DAC"}, 140 {"Headphone Jack", NULL, "Headphone Amplifier"}, 141}; 142 143static const struct snd_soc_dapm_route omap3pandora_in_map[] = { 144 {"AUXL", NULL, "Line In"}, 145 {"AUXR", NULL, "Line In"}, 146 147 {"MAINMIC", NULL, "Mic Bias 1"}, 148 {"Mic Bias 1", NULL, "Mic (internal)"}, 149 150 {"SUBMIC", NULL, "Mic Bias 2"}, 151 {"Mic Bias 2", NULL, "Mic (external)"}, 152}; 153 154static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) 155{ 156 struct snd_soc_codec *codec = rtd->codec; 157 struct snd_soc_dapm_context *dapm = &codec->dapm; 158 int ret; 159 160 /* All TWL4030 output pins are floating */ 161 snd_soc_dapm_nc_pin(dapm, "EARPIECE"); 162 snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); 163 snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); 164 snd_soc_dapm_nc_pin(dapm, "HSOL"); 165 snd_soc_dapm_nc_pin(dapm, "HSOR"); 166 snd_soc_dapm_nc_pin(dapm, "CARKITL"); 167 snd_soc_dapm_nc_pin(dapm, "CARKITR"); 168 snd_soc_dapm_nc_pin(dapm, "HFL"); 169 snd_soc_dapm_nc_pin(dapm, "HFR"); 170 snd_soc_dapm_nc_pin(dapm, "VIBRA"); 171 172 ret = snd_soc_dapm_new_controls(dapm, omap3pandora_out_dapm_widgets, 173 ARRAY_SIZE(omap3pandora_out_dapm_widgets)); 174 if (ret < 0) 175 return ret; 176 177 return snd_soc_dapm_add_routes(dapm, omap3pandora_out_map, 178 ARRAY_SIZE(omap3pandora_out_map)); 179} 180 181static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) 182{ 183 struct snd_soc_codec *codec = rtd->codec; 184 struct snd_soc_dapm_context *dapm = &codec->dapm; 185 int ret; 186 187 /* Not comnnected */ 188 snd_soc_dapm_nc_pin(dapm, "HSMIC"); 189 snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); 190 snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); 191 snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); 192 193 ret = snd_soc_dapm_new_controls(dapm, omap3pandora_in_dapm_widgets, 194 ARRAY_SIZE(omap3pandora_in_dapm_widgets)); 195 if (ret < 0) 196 return ret; 197 198 return snd_soc_dapm_add_routes(dapm, omap3pandora_in_map, 199 ARRAY_SIZE(omap3pandora_in_map)); 200} 201 202static struct snd_soc_ops omap3pandora_ops = { 203 .hw_params = omap3pandora_hw_params, 204}; 205 206/* Digital audio interface glue - connects codec <--> CPU */ 207static struct snd_soc_dai_link omap3pandora_dai[] = { 208 { 209 .name = "PCM1773", 210 .stream_name = "HiFi Out", 211 .cpu_dai_name = "omap-mcbsp-dai.1", 212 .codec_dai_name = "twl4030-hifi", 213 .platform_name = "omap-pcm-audio", 214 .codec_name = "twl4030-codec", 215 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 216 SND_SOC_DAIFMT_CBS_CFS, 217 .ops = &omap3pandora_ops, 218 .init = omap3pandora_out_init, 219 }, { 220 .name = "TWL4030", 221 .stream_name = "Line/Mic In", 222 .cpu_dai_name = "omap-mcbsp-dai.3", 223 .codec_dai_name = "twl4030-hifi", 224 .platform_name = "omap-pcm-audio", 225 .codec_name = "twl4030-codec", 226 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 227 SND_SOC_DAIFMT_CBS_CFS, 228 .ops = &omap3pandora_ops, 229 .init = omap3pandora_in_init, 230 } 231}; 232 233/* SoC card */ 234static struct snd_soc_card snd_soc_card_omap3pandora = { 235 .name = "omap3pandora", 236 .dai_link = omap3pandora_dai, 237 .num_links = ARRAY_SIZE(omap3pandora_dai), 238}; 239 240static struct platform_device *omap3pandora_snd_device; 241 242static int __init omap3pandora_soc_init(void) 243{ 244 int ret; 245 246 if (!machine_is_omap3_pandora()) 247 return -ENODEV; 248 249 pr_info("OMAP3 Pandora SoC init\n"); 250 251 ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power"); 252 if (ret) { 253 pr_err(PREFIX "Failed to get DAC power GPIO\n"); 254 return ret; 255 } 256 257 ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0); 258 if (ret) { 259 pr_err(PREFIX "Failed to set DAC power GPIO direction\n"); 260 goto fail0; 261 } 262 263 ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power"); 264 if (ret) { 265 pr_err(PREFIX "Failed to get amp power GPIO\n"); 266 goto fail0; 267 } 268 269 ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0); 270 if (ret) { 271 pr_err(PREFIX "Failed to set amp power GPIO direction\n"); 272 goto fail1; 273 } 274 275 omap3pandora_snd_device = platform_device_alloc("soc-audio", -1); 276 if (omap3pandora_snd_device == NULL) { 277 pr_err(PREFIX "Platform device allocation failed\n"); 278 ret = -ENOMEM; 279 goto fail1; 280 } 281 282 platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora); 283 284 ret = platform_device_add(omap3pandora_snd_device); 285 if (ret) { 286 pr_err(PREFIX "Unable to add platform device\n"); 287 goto fail2; 288 } 289 290 omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); 291 if (IS_ERR(omap3pandora_dac_reg)) { 292 pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", 293 dev_name(&omap3pandora_snd_device->dev), 294 PTR_ERR(omap3pandora_dac_reg)); 295 ret = PTR_ERR(omap3pandora_dac_reg); 296 goto fail3; 297 } 298 299 return 0; 300 301fail3: 302 platform_device_del(omap3pandora_snd_device); 303fail2: 304 platform_device_put(omap3pandora_snd_device); 305fail1: 306 gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); 307fail0: 308 gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); 309 return ret; 310} 311module_init(omap3pandora_soc_init); 312 313static void __exit omap3pandora_soc_exit(void) 314{ 315 regulator_put(omap3pandora_dac_reg); 316 platform_device_unregister(omap3pandora_snd_device); 317 gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); 318 gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); 319} 320module_exit(omap3pandora_soc_exit); 321 322MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>"); 323MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora"); 324MODULE_LICENSE("GPL");