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 = rtd->codec_dai; 36 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 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 static struct snd_soc_dai_link omap3pandora_dai[] = { 179 { 180 .name = "PCM1773", 181 .stream_name = "HiFi Out", 182 .cpu_dai_name = "omap-mcbsp.2", 183 .codec_dai_name = "twl4030-hifi", 184 .platform_name = "omap-mcbsp.2", 185 .codec_name = "twl4030-codec", 186 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 187 SND_SOC_DAIFMT_CBS_CFS, 188 .ops = &omap3pandora_ops, 189 .init = omap3pandora_out_init, 190 }, { 191 .name = "TWL4030", 192 .stream_name = "Line/Mic In", 193 .cpu_dai_name = "omap-mcbsp.4", 194 .codec_dai_name = "twl4030-hifi", 195 .platform_name = "omap-mcbsp.4", 196 .codec_name = "twl4030-codec", 197 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 198 SND_SOC_DAIFMT_CBS_CFS, 199 .ops = &omap3pandora_ops, 200 .init = omap3pandora_in_init, 201 } 202 }; 203 204 /* SoC card */ 205 static struct snd_soc_card snd_soc_card_omap3pandora = { 206 .name = "omap3pandora", 207 .owner = THIS_MODULE, 208 .dai_link = omap3pandora_dai, 209 .num_links = ARRAY_SIZE(omap3pandora_dai), 210 211 .dapm_widgets = omap3pandora_dapm_widgets, 212 .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets), 213 .dapm_routes = omap3pandora_map, 214 .num_dapm_routes = ARRAY_SIZE(omap3pandora_map), 215 }; 216 217 static struct platform_device *omap3pandora_snd_device; 218 219 static int __init omap3pandora_soc_init(void) 220 { 221 int ret; 222 223 if (!machine_is_omap3_pandora()) 224 return -ENODEV; 225 226 pr_info("OMAP3 Pandora SoC init\n"); 227 228 ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power"); 229 if (ret) { 230 pr_err(PREFIX "Failed to get DAC power GPIO\n"); 231 return ret; 232 } 233 234 ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0); 235 if (ret) { 236 pr_err(PREFIX "Failed to set DAC power GPIO direction\n"); 237 goto fail0; 238 } 239 240 ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power"); 241 if (ret) { 242 pr_err(PREFIX "Failed to get amp power GPIO\n"); 243 goto fail0; 244 } 245 246 ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0); 247 if (ret) { 248 pr_err(PREFIX "Failed to set amp power GPIO direction\n"); 249 goto fail1; 250 } 251 252 omap3pandora_snd_device = platform_device_alloc("soc-audio", -1); 253 if (omap3pandora_snd_device == NULL) { 254 pr_err(PREFIX "Platform device allocation failed\n"); 255 ret = -ENOMEM; 256 goto fail1; 257 } 258 259 platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora); 260 261 ret = platform_device_add(omap3pandora_snd_device); 262 if (ret) { 263 pr_err(PREFIX "Unable to add platform device\n"); 264 goto fail2; 265 } 266 267 omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); 268 if (IS_ERR(omap3pandora_dac_reg)) { 269 pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", 270 dev_name(&omap3pandora_snd_device->dev), 271 PTR_ERR(omap3pandora_dac_reg)); 272 ret = PTR_ERR(omap3pandora_dac_reg); 273 goto fail3; 274 } 275 276 return 0; 277 278 fail3: 279 platform_device_del(omap3pandora_snd_device); 280 fail2: 281 platform_device_put(omap3pandora_snd_device); 282 fail1: 283 gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); 284 fail0: 285 gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); 286 return ret; 287 } 288 module_init(omap3pandora_soc_init); 289 290 static void __exit omap3pandora_soc_exit(void) 291 { 292 regulator_put(omap3pandora_dac_reg); 293 platform_device_unregister(omap3pandora_snd_device); 294 gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); 295 gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); 296 } 297 module_exit(omap3pandora_soc_exit); 298 299 MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>"); 300 MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora"); 301 MODULE_LICENSE("GPL"); 302