linux/sound/soc/ti/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, "HS Mic");
  84        else
  85                snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
  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 const 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        SND_SOC_DAPM_MIC("HS Mic", NULL),
 226};
 227
 228static const struct snd_soc_dapm_route audio_map[] = {
 229        {"Headphone Jack", NULL, "HPLOUT"},
 230        {"Headphone Jack", NULL, "HPROUT"},
 231
 232        {"Ext Spk", NULL, "LLOUT"},
 233        {"Ext Spk", NULL, "RLOUT"},
 234
 235        {"DMic Rate 64", NULL, "DMic"},
 236        {"DMic", NULL, "Mic Bias"},
 237
 238        /*
 239         * Note that the mic bias is coming from Retu/Vilma and we don't have
 240         * control over it atm. The analog HS mic is not working. <- TODO
 241         */
 242        {"LINE1L", NULL, "HS Mic"},
 243};
 244
 245static const char *spk_function[] = {"Off", "On"};
 246static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
 247static const char *input_function[] = {"ADC", "Digital Mic"};
 248static const struct soc_enum n810_enum[] = {
 249        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 250        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
 251        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
 252};
 253
 254static const struct snd_kcontrol_new aic33_n810_controls[] = {
 255        SOC_ENUM_EXT("Speaker Function", n810_enum[0],
 256                     n810_get_spk, n810_set_spk),
 257        SOC_ENUM_EXT("Jack Function", n810_enum[1],
 258                     n810_get_jack, n810_set_jack),
 259        SOC_ENUM_EXT("Input Select",  n810_enum[2],
 260                     n810_get_input, n810_set_input),
 261};
 262
 263/* Digital audio interface glue - connects codec <--> CPU */
 264static struct snd_soc_dai_link n810_dai = {
 265        .name = "TLV320AIC33",
 266        .stream_name = "AIC33",
 267        .cpu_dai_name = "48076000.mcbsp",
 268        .platform_name = "48076000.mcbsp",
 269        .codec_name = "tlv320aic3x-codec.1-0018",
 270        .codec_dai_name = "tlv320aic3x-hifi",
 271        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 272                   SND_SOC_DAIFMT_CBM_CFM,
 273        .ops = &n810_ops,
 274};
 275
 276/* Audio machine driver */
 277static struct snd_soc_card snd_soc_n810 = {
 278        .name = "N810",
 279        .owner = THIS_MODULE,
 280        .dai_link = &n810_dai,
 281        .num_links = 1,
 282
 283        .controls = aic33_n810_controls,
 284        .num_controls = ARRAY_SIZE(aic33_n810_controls),
 285        .dapm_widgets = aic33_dapm_widgets,
 286        .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
 287        .dapm_routes = audio_map,
 288        .num_dapm_routes = ARRAY_SIZE(audio_map),
 289        .fully_routed = true,
 290};
 291
 292static struct platform_device *n810_snd_device;
 293
 294static int __init n810_soc_init(void)
 295{
 296        int err;
 297        struct device *dev;
 298
 299        if (!of_have_populated_dt() ||
 300            (!of_machine_is_compatible("nokia,n810") &&
 301             !of_machine_is_compatible("nokia,n810-wimax")))
 302                return -ENODEV;
 303
 304        n810_snd_device = platform_device_alloc("soc-audio", -1);
 305        if (!n810_snd_device)
 306                return -ENOMEM;
 307
 308        platform_set_drvdata(n810_snd_device, &snd_soc_n810);
 309        err = platform_device_add(n810_snd_device);
 310        if (err)
 311                goto err1;
 312
 313        dev = &n810_snd_device->dev;
 314
 315        sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
 316        if (IS_ERR(sys_clkout2_src)) {
 317                dev_err(dev, "Could not get sys_clkout2_src clock\n");
 318                err = PTR_ERR(sys_clkout2_src);
 319                goto err2;
 320        }
 321        sys_clkout2 = clk_get(dev, "sys_clkout2");
 322        if (IS_ERR(sys_clkout2)) {
 323                dev_err(dev, "Could not get sys_clkout2\n");
 324                err = PTR_ERR(sys_clkout2);
 325                goto err3;
 326        }
 327        /*
 328         * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
 329         * 96 MHz as its parent in order to get 12 MHz
 330         */
 331        func96m_clk = clk_get(dev, "func_96m_ck");
 332        if (IS_ERR(func96m_clk)) {
 333                dev_err(dev, "Could not get func 96M clock\n");
 334                err = PTR_ERR(func96m_clk);
 335                goto err4;
 336        }
 337        clk_set_parent(sys_clkout2_src, func96m_clk);
 338        clk_set_rate(sys_clkout2, 12000000);
 339
 340        if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
 341                    (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
 342                err = -EINVAL;
 343                goto err4;
 344        }
 345
 346        gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
 347        gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 348
 349        return 0;
 350err4:
 351        clk_put(sys_clkout2);
 352err3:
 353        clk_put(sys_clkout2_src);
 354err2:
 355        platform_device_del(n810_snd_device);
 356err1:
 357        platform_device_put(n810_snd_device);
 358
 359        return err;
 360}
 361
 362static void __exit n810_soc_exit(void)
 363{
 364        gpio_free(N810_SPEAKER_AMP_GPIO);
 365        gpio_free(N810_HEADSET_AMP_GPIO);
 366        clk_put(sys_clkout2_src);
 367        clk_put(sys_clkout2);
 368        clk_put(func96m_clk);
 369
 370        platform_device_unregister(n810_snd_device);
 371}
 372
 373module_init(n810_soc_init);
 374module_exit(n810_soc_exit);
 375
 376MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
 377MODULE_DESCRIPTION("ALSA SoC Nokia N810");
 378MODULE_LICENSE("GPL");
 379