linux/sound/soc/omap/omap3pandora.c
<<
>>
Prefs
   1/*
   2 * omap3pandora.c  --  SoC audio for Pandora Handheld Console
   3 *
   4 * Author: Gražvydas Ignotas <notasas@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * version 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18 * 02110-1301 USA
  19 *
  20 */
  21
  22#include <linux/clk.h>
  23#include <linux/platform_device.h>
  24#include <linux/gpio.h>
  25#include <linux/delay.h>
  26#include <linux/regulator/consumer.h>
  27#include <linux/module.h>
  28
  29#include <sound/core.h>
  30#include <sound/pcm.h>
  31#include <sound/soc.h>
  32
  33#include <asm/mach-types.h>
  34#include <linux/platform_data/asoc-ti-mcbsp.h>
  35
  36#include "omap-mcbsp.h"
  37
  38#define OMAP3_PANDORA_DAC_POWER_GPIO    118
  39#define OMAP3_PANDORA_AMP_POWER_GPIO    14
  40
  41#define PREFIX "ASoC omap3pandora: "
  42
  43static struct regulator *omap3pandora_dac_reg;
  44
  45static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
  46        struct snd_pcm_hw_params *params)
  47{
  48        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  49        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  50        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  51        int ret;
  52
  53        /* Set the codec system clock for DAC and ADC */
  54        ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
  55                                            SND_SOC_CLOCK_IN);
  56        if (ret < 0) {
  57                pr_err(PREFIX "can't set codec system clock\n");
  58                return ret;
  59        }
  60
  61        /* Set McBSP clock to external */
  62        ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
  63                                     256 * params_rate(params),
  64                                     SND_SOC_CLOCK_IN);
  65        if (ret < 0) {
  66                pr_err(PREFIX "can't set cpu system clock\n");
  67                return ret;
  68        }
  69
  70        ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
  71        if (ret < 0) {
  72                pr_err(PREFIX "can't set SRG clock divider\n");
  73                return ret;
  74        }
  75
  76        return 0;
  77}
  78
  79static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
  80        struct snd_kcontrol *k, int event)
  81{
  82        int ret;
  83
  84        /*
  85         * The PCM1773 DAC datasheet requires 1ms delay between switching
  86         * VCC power on/off and /PD pin high/low
  87         */
  88        if (SND_SOC_DAPM_EVENT_ON(event)) {
  89                ret = regulator_enable(omap3pandora_dac_reg);
  90                if (ret) {
  91                        dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);
  92                        return ret;
  93                }
  94                mdelay(1);
  95                gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
  96        } else {
  97                gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
  98                mdelay(1);
  99                regulator_disable(omap3pandora_dac_reg);
 100        }
 101
 102        return 0;
 103}
 104
 105static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
 106        struct snd_kcontrol *k, int event)
 107{
 108        if (SND_SOC_DAPM_EVENT_ON(event))
 109                gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
 110        else
 111                gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
 112
 113        return 0;
 114}
 115
 116/*
 117 * Audio paths on Pandora board:
 118 *
 119 *  |O| ---> PCM DAC +-> AMP -> Headphone Jack
 120 *  |M|         A    +--------> Line Out
 121 *  |A| <~~clk~~+
 122 *  |P| <--- TWL4030 <--------- Line In and MICs
 123 */
 124static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
 125        SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
 126                           0, 0, omap3pandora_dac_event,
 127                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 128        SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
 129                           0, 0, NULL, 0, omap3pandora_hp_event,
 130                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 131        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 132        SND_SOC_DAPM_LINE("Line Out", NULL),
 133
 134        SND_SOC_DAPM_MIC("Mic (internal)", NULL),
 135        SND_SOC_DAPM_MIC("Mic (external)", NULL),
 136        SND_SOC_DAPM_LINE("Line In", NULL),
 137};
 138
 139static const struct snd_soc_dapm_route omap3pandora_map[] = {
 140        {"PCM DAC", NULL, "APLL Enable"},
 141        {"Headphone Amplifier", NULL, "PCM DAC"},
 142        {"Line Out", NULL, "PCM DAC"},
 143        {"Headphone Jack", NULL, "Headphone Amplifier"},
 144
 145        {"AUXL", NULL, "Line In"},
 146        {"AUXR", NULL, "Line In"},
 147
 148        {"MAINMIC", NULL, "Mic (internal)"},
 149        {"Mic (internal)", NULL, "Mic Bias 1"},
 150
 151        {"SUBMIC", NULL, "Mic (external)"},
 152        {"Mic (external)", NULL, "Mic Bias 2"},
 153};
 154
 155static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
 156{
 157        struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
 158
 159        /* All TWL4030 output pins are floating */
 160        snd_soc_dapm_nc_pin(dapm, "EARPIECE");
 161        snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
 162        snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
 163        snd_soc_dapm_nc_pin(dapm, "HSOL");
 164        snd_soc_dapm_nc_pin(dapm, "HSOR");
 165        snd_soc_dapm_nc_pin(dapm, "CARKITL");
 166        snd_soc_dapm_nc_pin(dapm, "CARKITR");
 167        snd_soc_dapm_nc_pin(dapm, "HFL");
 168        snd_soc_dapm_nc_pin(dapm, "HFR");
 169        snd_soc_dapm_nc_pin(dapm, "VIBRA");
 170
 171        return 0;
 172}
 173
 174static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
 175{
 176        struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
 177
 178        /* Not comnnected */
 179        snd_soc_dapm_nc_pin(dapm, "HSMIC");
 180        snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
 181        snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
 182        snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
 183
 184        return 0;
 185}
 186
 187static const struct snd_soc_ops omap3pandora_ops = {
 188        .hw_params = omap3pandora_hw_params,
 189};
 190
 191/* Digital audio interface glue - connects codec <--> CPU */
 192static struct snd_soc_dai_link omap3pandora_dai[] = {
 193        {
 194                .name = "PCM1773",
 195                .stream_name = "HiFi Out",
 196                .cpu_dai_name = "omap-mcbsp.2",
 197                .codec_dai_name = "twl4030-hifi",
 198                .platform_name = "omap-mcbsp.2",
 199                .codec_name = "twl4030-codec",
 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_out_init,
 204        }, {
 205                .name = "TWL4030",
 206                .stream_name = "Line/Mic In",
 207                .cpu_dai_name = "omap-mcbsp.4",
 208                .codec_dai_name = "twl4030-hifi",
 209                .platform_name = "omap-mcbsp.4",
 210                .codec_name = "twl4030-codec",
 211                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 212                           SND_SOC_DAIFMT_CBS_CFS,
 213                .ops = &omap3pandora_ops,
 214                .init = omap3pandora_in_init,
 215        }
 216};
 217
 218/* SoC card */
 219static struct snd_soc_card snd_soc_card_omap3pandora = {
 220        .name = "omap3pandora",
 221        .owner = THIS_MODULE,
 222        .dai_link = omap3pandora_dai,
 223        .num_links = ARRAY_SIZE(omap3pandora_dai),
 224
 225        .dapm_widgets = omap3pandora_dapm_widgets,
 226        .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
 227        .dapm_routes = omap3pandora_map,
 228        .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
 229};
 230
 231static struct platform_device *omap3pandora_snd_device;
 232
 233static int __init omap3pandora_soc_init(void)
 234{
 235        int ret;
 236
 237        if (!machine_is_omap3_pandora())
 238                return -ENODEV;
 239
 240        pr_info("OMAP3 Pandora SoC init\n");
 241
 242        ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
 243        if (ret) {
 244                pr_err(PREFIX "Failed to get DAC power GPIO\n");
 245                return ret;
 246        }
 247
 248        ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
 249        if (ret) {
 250                pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
 251                goto fail0;
 252        }
 253
 254        ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
 255        if (ret) {
 256                pr_err(PREFIX "Failed to get amp power GPIO\n");
 257                goto fail0;
 258        }
 259
 260        ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
 261        if (ret) {
 262                pr_err(PREFIX "Failed to set amp power GPIO direction\n");
 263                goto fail1;
 264        }
 265
 266        omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
 267        if (omap3pandora_snd_device == NULL) {
 268                pr_err(PREFIX "Platform device allocation failed\n");
 269                ret = -ENOMEM;
 270                goto fail1;
 271        }
 272
 273        platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
 274
 275        ret = platform_device_add(omap3pandora_snd_device);
 276        if (ret) {
 277                pr_err(PREFIX "Unable to add platform device\n");
 278                goto fail2;
 279        }
 280
 281        omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
 282        if (IS_ERR(omap3pandora_dac_reg)) {
 283                pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
 284                        dev_name(&omap3pandora_snd_device->dev),
 285                        PTR_ERR(omap3pandora_dac_reg));
 286                ret = PTR_ERR(omap3pandora_dac_reg);
 287                goto fail3;
 288        }
 289
 290        return 0;
 291
 292fail3:
 293        platform_device_del(omap3pandora_snd_device);
 294fail2:
 295        platform_device_put(omap3pandora_snd_device);
 296fail1:
 297        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 298fail0:
 299        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 300        return ret;
 301}
 302module_init(omap3pandora_soc_init);
 303
 304static void __exit omap3pandora_soc_exit(void)
 305{
 306        regulator_put(omap3pandora_dac_reg);
 307        platform_device_unregister(omap3pandora_snd_device);
 308        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 309        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 310}
 311module_exit(omap3pandora_soc_exit);
 312
 313MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
 314MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
 315MODULE_LICENSE("GPL");
 316