linux/sound/soc/ti/omap3pandora.c
<<
>>
Prefs
   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
  29static struct regulator *omap3pandora_dac_reg;
  30
  31static 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
  65static 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
  91static 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 */
 110static 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
 125static 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
 141static 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
 160static 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
 173static const struct snd_soc_ops omap3pandora_ops = {
 174        .hw_params = omap3pandora_hw_params,
 175};
 176
 177/* Digital audio interface glue - connects codec <--> CPU */
 178static 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 */
 205static 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
 217static struct platform_device *omap3pandora_snd_device;
 218
 219static 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
 278fail3:
 279        platform_device_del(omap3pandora_snd_device);
 280fail2:
 281        platform_device_put(omap3pandora_snd_device);
 282fail1:
 283        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 284fail0:
 285        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 286        return ret;
 287}
 288module_init(omap3pandora_soc_init);
 289
 290static 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}
 297module_exit(omap3pandora_soc_exit);
 298
 299MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
 300MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
 301MODULE_LICENSE("GPL");
 302