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_codec *codec = rtd->codec;
 158        struct snd_soc_dapm_context *dapm = &codec->dapm;
 159
 160        /* All TWL4030 output pins are floating */
 161        snd_soc_dapm_nc_pin(dapm, "EARPIECE");
 162        snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
 163        snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
 164        snd_soc_dapm_nc_pin(dapm, "HSOL");
 165        snd_soc_dapm_nc_pin(dapm, "HSOR");
 166        snd_soc_dapm_nc_pin(dapm, "CARKITL");
 167        snd_soc_dapm_nc_pin(dapm, "CARKITR");
 168        snd_soc_dapm_nc_pin(dapm, "HFL");
 169        snd_soc_dapm_nc_pin(dapm, "HFR");
 170        snd_soc_dapm_nc_pin(dapm, "VIBRA");
 171
 172        return 0;
 173}
 174
 175static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
 176{
 177        struct snd_soc_codec *codec = rtd->codec;
 178        struct snd_soc_dapm_context *dapm = &codec->dapm;
 179
 180        /* Not comnnected */
 181        snd_soc_dapm_nc_pin(dapm, "HSMIC");
 182        snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
 183        snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
 184        snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
 185
 186        return 0;
 187}
 188
 189static struct snd_soc_ops omap3pandora_ops = {
 190        .hw_params = omap3pandora_hw_params,
 191};
 192
 193/* Digital audio interface glue - connects codec <--> CPU */
 194static struct snd_soc_dai_link omap3pandora_dai[] = {
 195        {
 196                .name = "PCM1773",
 197                .stream_name = "HiFi Out",
 198                .cpu_dai_name = "omap-mcbsp.2",
 199                .codec_dai_name = "twl4030-hifi",
 200                .platform_name = "omap-mcbsp.2",
 201                .codec_name = "twl4030-codec",
 202                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 203                           SND_SOC_DAIFMT_CBS_CFS,
 204                .ops = &omap3pandora_ops,
 205                .init = omap3pandora_out_init,
 206        }, {
 207                .name = "TWL4030",
 208                .stream_name = "Line/Mic In",
 209                .cpu_dai_name = "omap-mcbsp.4",
 210                .codec_dai_name = "twl4030-hifi",
 211                .platform_name = "omap-mcbsp.4",
 212                .codec_name = "twl4030-codec",
 213                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 214                           SND_SOC_DAIFMT_CBS_CFS,
 215                .ops = &omap3pandora_ops,
 216                .init = omap3pandora_in_init,
 217        }
 218};
 219
 220/* SoC card */
 221static struct snd_soc_card snd_soc_card_omap3pandora = {
 222        .name = "omap3pandora",
 223        .owner = THIS_MODULE,
 224        .dai_link = omap3pandora_dai,
 225        .num_links = ARRAY_SIZE(omap3pandora_dai),
 226
 227        .dapm_widgets = omap3pandora_dapm_widgets,
 228        .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
 229        .dapm_routes = omap3pandora_map,
 230        .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
 231};
 232
 233static struct platform_device *omap3pandora_snd_device;
 234
 235static int __init omap3pandora_soc_init(void)
 236{
 237        int ret;
 238
 239        if (!machine_is_omap3_pandora())
 240                return -ENODEV;
 241
 242        pr_info("OMAP3 Pandora SoC init\n");
 243
 244        ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
 245        if (ret) {
 246                pr_err(PREFIX "Failed to get DAC power GPIO\n");
 247                return ret;
 248        }
 249
 250        ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
 251        if (ret) {
 252                pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
 253                goto fail0;
 254        }
 255
 256        ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
 257        if (ret) {
 258                pr_err(PREFIX "Failed to get amp power GPIO\n");
 259                goto fail0;
 260        }
 261
 262        ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
 263        if (ret) {
 264                pr_err(PREFIX "Failed to set amp power GPIO direction\n");
 265                goto fail1;
 266        }
 267
 268        omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
 269        if (omap3pandora_snd_device == NULL) {
 270                pr_err(PREFIX "Platform device allocation failed\n");
 271                ret = -ENOMEM;
 272                goto fail1;
 273        }
 274
 275        platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
 276
 277        ret = platform_device_add(omap3pandora_snd_device);
 278        if (ret) {
 279                pr_err(PREFIX "Unable to add platform device\n");
 280                goto fail2;
 281        }
 282
 283        omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
 284        if (IS_ERR(omap3pandora_dac_reg)) {
 285                pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
 286                        dev_name(&omap3pandora_snd_device->dev),
 287                        PTR_ERR(omap3pandora_dac_reg));
 288                ret = PTR_ERR(omap3pandora_dac_reg);
 289                goto fail3;
 290        }
 291
 292        return 0;
 293
 294fail3:
 295        platform_device_del(omap3pandora_snd_device);
 296fail2:
 297        platform_device_put(omap3pandora_snd_device);
 298fail1:
 299        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 300fail0:
 301        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 302        return ret;
 303}
 304module_init(omap3pandora_soc_init);
 305
 306static void __exit omap3pandora_soc_exit(void)
 307{
 308        regulator_put(omap3pandora_dac_reg);
 309        platform_device_unregister(omap3pandora_snd_device);
 310        gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 311        gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
 312}
 313module_exit(omap3pandora_soc_exit);
 314
 315MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
 316MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
 317MODULE_LICENSE("GPL");
 318