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