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