linux/sound/soc/omap/n810.c
<<
>>
Prefs
   1/*
   2 * n810.c  --  SoC audio for Nokia N810
   3 *
   4 * Copyright (C) 2008 Nokia Corporation
   5 *
   6 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * version 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA
  21 *
  22 */
  23
  24#include <linux/clk.h>
  25#include <linux/i2c.h>
  26#include <linux/platform_device.h>
  27#include <sound/core.h>
  28#include <sound/pcm.h>
  29#include <sound/soc.h>
  30
  31#include <asm/mach-types.h>
  32#include <linux/gpio.h>
  33#include <linux/module.h>
  34#include <linux/platform_data/asoc-ti-mcbsp.h>
  35
  36#include "omap-mcbsp.h"
  37
  38#define N810_HEADSET_AMP_GPIO   10
  39#define N810_SPEAKER_AMP_GPIO   101
  40
  41enum {
  42        N810_JACK_DISABLED,
  43        N810_JACK_HP,
  44        N810_JACK_HS,
  45        N810_JACK_MIC,
  46};
  47
  48static struct clk *sys_clkout2;
  49static struct clk *sys_clkout2_src;
  50static struct clk *func96m_clk;
  51
  52static int n810_spk_func;
  53static int n810_jack_func;
  54static int n810_dmic_func;
  55
  56static void n810_ext_control(struct snd_soc_dapm_context *dapm)
  57{
  58        int hp = 0, line1l = 0;
  59
  60        switch (n810_jack_func) {
  61        case N810_JACK_HS:
  62                line1l = 1;
  63        case N810_JACK_HP:
  64                hp = 1;
  65                break;
  66        case N810_JACK_MIC:
  67                line1l = 1;
  68                break;
  69        }
  70
  71        snd_soc_dapm_mutex_lock(dapm);
  72
  73        if (n810_spk_func)
  74                snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
  75        else
  76                snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
  77
  78        if (hp)
  79                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  80        else
  81                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  82        if (line1l)
  83                snd_soc_dapm_enable_pin_unlocked(dapm, "LINE1L");
  84        else
  85                snd_soc_dapm_disable_pin_unlocked(dapm, "LINE1L");
  86
  87        if (n810_dmic_func)
  88                snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
  89        else
  90                snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
  91
  92        snd_soc_dapm_sync_unlocked(dapm);
  93
  94        snd_soc_dapm_mutex_unlock(dapm);
  95}
  96
  97static int n810_startup(struct snd_pcm_substream *substream)
  98{
  99        struct snd_pcm_runtime *runtime = substream->runtime;
 100        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 101
 102        snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 103
 104        n810_ext_control(&rtd->card->dapm);
 105        return clk_prepare_enable(sys_clkout2);
 106}
 107
 108static void n810_shutdown(struct snd_pcm_substream *substream)
 109{
 110        clk_disable_unprepare(sys_clkout2);
 111}
 112
 113static int n810_hw_params(struct snd_pcm_substream *substream,
 114        struct snd_pcm_hw_params *params)
 115{
 116        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 117        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 118        int err;
 119
 120        /* Set the codec system clock for DAC and ADC */
 121        err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
 122                                            SND_SOC_CLOCK_IN);
 123
 124        return err;
 125}
 126
 127static struct snd_soc_ops n810_ops = {
 128        .startup = n810_startup,
 129        .hw_params = n810_hw_params,
 130        .shutdown = n810_shutdown,
 131};
 132
 133static int n810_get_spk(struct snd_kcontrol *kcontrol,
 134                        struct snd_ctl_elem_value *ucontrol)
 135{
 136        ucontrol->value.enumerated.item[0] = n810_spk_func;
 137
 138        return 0;
 139}
 140
 141static int n810_set_spk(struct snd_kcontrol *kcontrol,
 142                        struct snd_ctl_elem_value *ucontrol)
 143{
 144        struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
 145
 146        if (n810_spk_func == ucontrol->value.enumerated.item[0])
 147                return 0;
 148
 149        n810_spk_func = ucontrol->value.enumerated.item[0];
 150        n810_ext_control(&card->dapm);
 151
 152        return 1;
 153}
 154
 155static int n810_get_jack(struct snd_kcontrol *kcontrol,
 156                         struct snd_ctl_elem_value *ucontrol)
 157{
 158        ucontrol->value.enumerated.item[0] = n810_jack_func;
 159
 160        return 0;
 161}
 162
 163static int n810_set_jack(struct snd_kcontrol *kcontrol,
 164                         struct snd_ctl_elem_value *ucontrol)
 165{
 166        struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
 167
 168        if (n810_jack_func == ucontrol->value.enumerated.item[0])
 169                return 0;
 170
 171        n810_jack_func = ucontrol->value.enumerated.item[0];
 172        n810_ext_control(&card->dapm);
 173
 174        return 1;
 175}
 176
 177static int n810_get_input(struct snd_kcontrol *kcontrol,
 178                          struct snd_ctl_elem_value *ucontrol)
 179{
 180        ucontrol->value.enumerated.item[0] = n810_dmic_func;
 181
 182        return 0;
 183}
 184
 185static int n810_set_input(struct snd_kcontrol *kcontrol,
 186                          struct snd_ctl_elem_value *ucontrol)
 187{
 188        struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
 189
 190        if (n810_dmic_func == ucontrol->value.enumerated.item[0])
 191                return 0;
 192
 193        n810_dmic_func = ucontrol->value.enumerated.item[0];
 194        n810_ext_control(&card->dapm);
 195
 196        return 1;
 197}
 198
 199static int n810_spk_event(struct snd_soc_dapm_widget *w,
 200                          struct snd_kcontrol *k, int event)
 201{
 202        if (SND_SOC_DAPM_EVENT_ON(event))
 203                gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
 204        else
 205                gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
 206
 207        return 0;
 208}
 209
 210static int n810_jack_event(struct snd_soc_dapm_widget *w,
 211                           struct snd_kcontrol *k, int event)
 212{
 213        if (SND_SOC_DAPM_EVENT_ON(event))
 214                gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
 215        else
 216                gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
 217
 218        return 0;
 219}
 220
 221static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
 222        SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
 223        SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
 224        SND_SOC_DAPM_MIC("DMic", NULL),
 225};
 226
 227static const struct snd_soc_dapm_route audio_map[] = {
 228        {"Headphone Jack", NULL, "HPLOUT"},
 229        {"Headphone Jack", NULL, "HPROUT"},
 230
 231        {"Ext Spk", NULL, "LLOUT"},
 232        {"Ext Spk", NULL, "RLOUT"},
 233
 234        {"DMic Rate 64", NULL, "Mic Bias"},
 235        {"Mic Bias", NULL, "DMic"},
 236};
 237
 238static const char *spk_function[] = {"Off", "On"};
 239static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
 240static const char *input_function[] = {"ADC", "Digital Mic"};
 241static const struct soc_enum n810_enum[] = {
 242        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 243        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
 244        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
 245};
 246
 247static const struct snd_kcontrol_new aic33_n810_controls[] = {
 248        SOC_ENUM_EXT("Speaker Function", n810_enum[0],
 249                     n810_get_spk, n810_set_spk),
 250        SOC_ENUM_EXT("Jack Function", n810_enum[1],
 251                     n810_get_jack, n810_set_jack),
 252        SOC_ENUM_EXT("Input Select",  n810_enum[2],
 253                     n810_get_input, n810_set_input),
 254};
 255
 256/* Digital audio interface glue - connects codec <--> CPU */
 257static struct snd_soc_dai_link n810_dai = {
 258        .name = "TLV320AIC33",
 259        .stream_name = "AIC33",
 260        .cpu_dai_name = "omap-mcbsp.2",
 261        .platform_name = "omap-mcbsp.2",
 262        .codec_name = "tlv320aic3x-codec.2-0018",
 263        .codec_dai_name = "tlv320aic3x-hifi",
 264        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 265                   SND_SOC_DAIFMT_CBM_CFM,
 266        .ops = &n810_ops,
 267};
 268
 269/* Audio machine driver */
 270static struct snd_soc_card snd_soc_n810 = {
 271        .name = "N810",
 272        .owner = THIS_MODULE,
 273        .dai_link = &n810_dai,
 274        .num_links = 1,
 275
 276        .controls = aic33_n810_controls,
 277        .num_controls = ARRAY_SIZE(aic33_n810_controls),
 278        .dapm_widgets = aic33_dapm_widgets,
 279        .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
 280        .dapm_routes = audio_map,
 281        .num_dapm_routes = ARRAY_SIZE(audio_map),
 282        .fully_routed = true,
 283};
 284
 285static struct platform_device *n810_snd_device;
 286
 287static int __init n810_soc_init(void)
 288{
 289        int err;
 290        struct device *dev;
 291
 292        if (!of_have_populated_dt() ||
 293            (!of_machine_is_compatible("nokia,n810") &&
 294             !of_machine_is_compatible("nokia,n810-wimax")))
 295                return -ENODEV;
 296
 297        n810_snd_device = platform_device_alloc("soc-audio", -1);
 298        if (!n810_snd_device)
 299                return -ENOMEM;
 300
 301        platform_set_drvdata(n810_snd_device, &snd_soc_n810);
 302        err = platform_device_add(n810_snd_device);
 303        if (err)
 304                goto err1;
 305
 306        dev = &n810_snd_device->dev;
 307
 308        sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
 309        if (IS_ERR(sys_clkout2_src)) {
 310                dev_err(dev, "Could not get sys_clkout2_src clock\n");
 311                err = PTR_ERR(sys_clkout2_src);
 312                goto err2;
 313        }
 314        sys_clkout2 = clk_get(dev, "sys_clkout2");
 315        if (IS_ERR(sys_clkout2)) {
 316                dev_err(dev, "Could not get sys_clkout2\n");
 317                err = PTR_ERR(sys_clkout2);
 318                goto err3;
 319        }
 320        /*
 321         * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
 322         * 96 MHz as its parent in order to get 12 MHz
 323         */
 324        func96m_clk = clk_get(dev, "func_96m_ck");
 325        if (IS_ERR(func96m_clk)) {
 326                dev_err(dev, "Could not get func 96M clock\n");
 327                err = PTR_ERR(func96m_clk);
 328                goto err4;
 329        }
 330        clk_set_parent(sys_clkout2_src, func96m_clk);
 331        clk_set_rate(sys_clkout2, 12000000);
 332
 333        if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
 334                    (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
 335                err = -EINVAL;
 336                goto err4;
 337        }
 338
 339        gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
 340        gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 341
 342        return 0;
 343err4:
 344        clk_put(sys_clkout2);
 345err3:
 346        clk_put(sys_clkout2_src);
 347err2:
 348        platform_device_del(n810_snd_device);
 349err1:
 350        platform_device_put(n810_snd_device);
 351
 352        return err;
 353}
 354
 355static void __exit n810_soc_exit(void)
 356{
 357        gpio_free(N810_SPEAKER_AMP_GPIO);
 358        gpio_free(N810_HEADSET_AMP_GPIO);
 359        clk_put(sys_clkout2_src);
 360        clk_put(sys_clkout2);
 361        clk_put(func96m_clk);
 362
 363        platform_device_unregister(n810_snd_device);
 364}
 365
 366module_init(n810_soc_init);
 367module_exit(n810_soc_exit);
 368
 369MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
 370MODULE_DESCRIPTION("ALSA SoC Nokia N810");
 371MODULE_LICENSE("GPL");
 372