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 = asoc_substream_to_rtd(substream);
  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
  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 */
 178SND_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
 183SND_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
 188static 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 */
 209static 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
 221static struct platform_device *omap3pandora_snd_device;
 222
 223static 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
 282fail3:
 283        platform_device_del(omap3pandora_snd_device);
 284fail2:
 285        platform_device_put(omap3pandora_snd_device);
 286fail1:
 287        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 288fail0:
 289        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 290        return ret;
 291}
 292module_init(omap3pandora_soc_init);
 293
 294static 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}
 301module_exit(omap3pandora_soc_exit);
 302
 303MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
 304MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
 305MODULE_LICENSE("GPL");
 306