289 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * omap3pandora.c -- SoC audio for Pandora Handheld Console4 *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 switching71 * VCC power on/off and /PD pin high/low72 */73 if (SND_SOC_DAPM_EVENT_ON(event)) {74 ret = regulator_enable(omap3pandora_dac_reg);75 if (ret) {76 dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);77 return ret;78 }79 mdelay(1);80 gpiod_set_value(dac_power_gpio, 1);81 } else {82 gpiod_set_value(dac_power_gpio, 0);83 mdelay(1);84 regulator_disable(omap3pandora_dac_reg);85 }86 87 return 0;88}89 90static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,91 struct snd_kcontrol *k, int event)92{93 if (SND_SOC_DAPM_EVENT_ON(event))94 gpiod_set_value(amp_power_gpio, 1);95 else96 gpiod_set_value(amp_power_gpio, 0);97 98 return 0;99}100 101/*102 * Audio paths on Pandora board:103 *104 * |O| ---> PCM DAC +-> AMP -> Headphone Jack105 * |M| A +--------> Line Out106 * |A| <~~clk~~+107 * |P| <--- TWL4030 <--------- Line In and MICs108 */109static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {110 SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,111 0, 0, omap3pandora_dac_event,112 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),113 SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,114 0, 0, NULL, 0, omap3pandora_hp_event,115 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),116 SND_SOC_DAPM_HP("Headphone Jack", NULL),117 SND_SOC_DAPM_LINE("Line Out", NULL),118 119 SND_SOC_DAPM_MIC("Mic (internal)", NULL),120 SND_SOC_DAPM_MIC("Mic (external)", NULL),121 SND_SOC_DAPM_LINE("Line In", NULL),122};123 124static const struct snd_soc_dapm_route omap3pandora_map[] = {125 {"PCM DAC", NULL, "APLL Enable"},126 {"Headphone Amplifier", NULL, "PCM DAC"},127 {"Line Out", NULL, "PCM DAC"},128 {"Headphone Jack", NULL, "Headphone Amplifier"},129 130 {"AUXL", NULL, "Line In"},131 {"AUXR", NULL, "Line In"},132 133 {"MAINMIC", NULL, "Mic (internal)"},134 {"Mic (internal)", NULL, "Mic Bias 1"},135 136 {"SUBMIC", NULL, "Mic (external)"},137 {"Mic (external)", NULL, "Mic Bias 2"},138};139 140static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)141{142 struct snd_soc_dapm_context *dapm = &rtd->card->dapm;143 144 /* All TWL4030 output pins are floating */145 snd_soc_dapm_nc_pin(dapm, "EARPIECE");146 snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");147 snd_soc_dapm_nc_pin(dapm, "PREDRIVER");148 snd_soc_dapm_nc_pin(dapm, "HSOL");149 snd_soc_dapm_nc_pin(dapm, "HSOR");150 snd_soc_dapm_nc_pin(dapm, "CARKITL");151 snd_soc_dapm_nc_pin(dapm, "CARKITR");152 snd_soc_dapm_nc_pin(dapm, "HFL");153 snd_soc_dapm_nc_pin(dapm, "HFR");154 snd_soc_dapm_nc_pin(dapm, "VIBRA");155 156 return 0;157}158 159static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)160{161 struct snd_soc_dapm_context *dapm = &rtd->card->dapm;162 163 /* Not comnnected */164 snd_soc_dapm_nc_pin(dapm, "HSMIC");165 snd_soc_dapm_nc_pin(dapm, "CARKITMIC");166 snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");167 snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");168 169 return 0;170}171 172static const struct snd_soc_ops omap3pandora_ops = {173 .hw_params = omap3pandora_hw_params,174};175 176/* Digital audio interface glue - connects codec <--> CPU */177SND_SOC_DAILINK_DEFS(out,178 DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.2")),179 DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),180 DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.2")));181 182SND_SOC_DAILINK_DEFS(in,183 DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.4")),184 DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),185 DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.4")));186 187static struct snd_soc_dai_link omap3pandora_dai[] = {188 {189 .name = "PCM1773",190 .stream_name = "HiFi Out",191 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |192 SND_SOC_DAIFMT_CBS_CFS,193 .ops = &omap3pandora_ops,194 .init = omap3pandora_out_init,195 SND_SOC_DAILINK_REG(out),196 }, {197 .name = "TWL4030",198 .stream_name = "Line/Mic In",199 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |200 SND_SOC_DAIFMT_CBS_CFS,201 .ops = &omap3pandora_ops,202 .init = omap3pandora_in_init,203 SND_SOC_DAILINK_REG(in),204 }205};206 207/* SoC card */208static struct snd_soc_card snd_soc_card_omap3pandora = {209 .name = "omap3pandora",210 .owner = THIS_MODULE,211 .dai_link = omap3pandora_dai,212 .num_links = ARRAY_SIZE(omap3pandora_dai),213 214 .dapm_widgets = omap3pandora_dapm_widgets,215 .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),216 .dapm_routes = omap3pandora_map,217 .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),218};219 220static struct platform_device *omap3pandora_snd_device;221 222static int __init omap3pandora_soc_init(void)223{224 int ret;225 226 if (!machine_is_omap3_pandora())227 return -ENODEV;228 229 pr_info("OMAP3 Pandora SoC init\n");230 231 omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);232 if (omap3pandora_snd_device == NULL) {233 pr_err(PREFIX "Platform device allocation failed\n");234 return -ENOMEM;235 }236 237 platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);238 239 ret = platform_device_add(omap3pandora_snd_device);240 if (ret) {241 pr_err(PREFIX "Unable to add platform device\n");242 goto fail2;243 }244 245 dac_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev,246 "dac", GPIOD_OUT_LOW);247 if (IS_ERR(dac_power_gpio)) {248 ret = PTR_ERR(dac_power_gpio);249 goto fail3;250 }251 252 amp_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev,253 "amp", GPIOD_OUT_LOW);254 if (IS_ERR(amp_power_gpio)) {255 ret = PTR_ERR(amp_power_gpio);256 goto fail3;257 }258 259 omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");260 if (IS_ERR(omap3pandora_dac_reg)) {261 pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",262 dev_name(&omap3pandora_snd_device->dev),263 PTR_ERR(omap3pandora_dac_reg));264 ret = PTR_ERR(omap3pandora_dac_reg);265 goto fail3;266 }267 268 return 0;269 270fail3:271 platform_device_del(omap3pandora_snd_device);272fail2:273 platform_device_put(omap3pandora_snd_device);274 275 return ret;276}277module_init(omap3pandora_soc_init);278 279static void __exit omap3pandora_soc_exit(void)280{281 regulator_put(omap3pandora_dac_reg);282 platform_device_unregister(omap3pandora_snd_device);283}284module_exit(omap3pandora_soc_exit);285 286MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");287MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");288MODULE_LICENSE("GPL");289