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