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 <jhnikula@gmail.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#include <sound/soc-dapm.h>
  31
  32#include <asm/mach-types.h>
  33#include <mach/hardware.h>
  34#include <linux/gpio.h>
  35#include <mach/mcbsp.h>
  36
  37#include "omap-mcbsp.h"
  38#include "omap-pcm.h"
  39#include "../codecs/tlv320aic3x.h"
  40
  41#define N810_HEADSET_AMP_GPIO   10
  42#define N810_SPEAKER_AMP_GPIO   101
  43
  44enum {
  45        N810_JACK_DISABLED,
  46        N810_JACK_HP,
  47        N810_JACK_HS,
  48        N810_JACK_MIC,
  49};
  50
  51static struct clk *sys_clkout2;
  52static struct clk *sys_clkout2_src;
  53static struct clk *func96m_clk;
  54
  55static int n810_spk_func;
  56static int n810_jack_func;
  57static int n810_dmic_func;
  58
  59static void n810_ext_control(struct snd_soc_codec *codec)
  60{
  61        int hp = 0, line1l = 0;
  62
  63        switch (n810_jack_func) {
  64        case N810_JACK_HS:
  65                line1l = 1;
  66        case N810_JACK_HP:
  67                hp = 1;
  68                break;
  69        case N810_JACK_MIC:
  70                line1l = 1;
  71                break;
  72        }
  73
  74        if (n810_spk_func)
  75                snd_soc_dapm_enable_pin(codec, "Ext Spk");
  76        else
  77                snd_soc_dapm_disable_pin(codec, "Ext Spk");
  78
  79        if (hp)
  80                snd_soc_dapm_enable_pin(codec, "Headphone Jack");
  81        else
  82                snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  83        if (line1l)
  84                snd_soc_dapm_enable_pin(codec, "LINE1L");
  85        else
  86                snd_soc_dapm_disable_pin(codec, "LINE1L");
  87
  88        if (n810_dmic_func)
  89                snd_soc_dapm_enable_pin(codec, "DMic");
  90        else
  91                snd_soc_dapm_disable_pin(codec, "DMic");
  92
  93        snd_soc_dapm_sync(codec);
  94}
  95
  96static int n810_startup(struct snd_pcm_substream *substream)
  97{
  98        struct snd_pcm_runtime *runtime = substream->runtime;
  99        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 100        struct snd_soc_codec *codec = rtd->socdev->card->codec;
 101
 102        snd_pcm_hw_constraint_minmax(runtime,
 103                                     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
 104
 105        n810_ext_control(codec);
 106        return clk_enable(sys_clkout2);
 107}
 108
 109static void n810_shutdown(struct snd_pcm_substream *substream)
 110{
 111        clk_disable(sys_clkout2);
 112}
 113
 114static int n810_hw_params(struct snd_pcm_substream *substream,
 115        struct snd_pcm_hw_params *params)
 116{
 117        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 118        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 119        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 120        int err;
 121
 122        /* Set codec DAI configuration */
 123        err = snd_soc_dai_set_fmt(codec_dai,
 124                                         SND_SOC_DAIFMT_I2S |
 125                                         SND_SOC_DAIFMT_NB_NF |
 126                                         SND_SOC_DAIFMT_CBM_CFM);
 127        if (err < 0)
 128                return err;
 129
 130        /* Set cpu DAI configuration */
 131        err = snd_soc_dai_set_fmt(cpu_dai,
 132                                       SND_SOC_DAIFMT_I2S |
 133                                       SND_SOC_DAIFMT_NB_NF |
 134                                       SND_SOC_DAIFMT_CBM_CFM);
 135        if (err < 0)
 136                return err;
 137
 138        /* Set the codec system clock for DAC and ADC */
 139        err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
 140                                            SND_SOC_CLOCK_IN);
 141
 142        return err;
 143}
 144
 145static struct snd_soc_ops n810_ops = {
 146        .startup = n810_startup,
 147        .hw_params = n810_hw_params,
 148        .shutdown = n810_shutdown,
 149};
 150
 151static int n810_get_spk(struct snd_kcontrol *kcontrol,
 152                        struct snd_ctl_elem_value *ucontrol)
 153{
 154        ucontrol->value.integer.value[0] = n810_spk_func;
 155
 156        return 0;
 157}
 158
 159static int n810_set_spk(struct snd_kcontrol *kcontrol,
 160                        struct snd_ctl_elem_value *ucontrol)
 161{
 162        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 163
 164        if (n810_spk_func == ucontrol->value.integer.value[0])
 165                return 0;
 166
 167        n810_spk_func = ucontrol->value.integer.value[0];
 168        n810_ext_control(codec);
 169
 170        return 1;
 171}
 172
 173static int n810_get_jack(struct snd_kcontrol *kcontrol,
 174                         struct snd_ctl_elem_value *ucontrol)
 175{
 176        ucontrol->value.integer.value[0] = n810_jack_func;
 177
 178        return 0;
 179}
 180
 181static int n810_set_jack(struct snd_kcontrol *kcontrol,
 182                         struct snd_ctl_elem_value *ucontrol)
 183{
 184        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 185
 186        if (n810_jack_func == ucontrol->value.integer.value[0])
 187                return 0;
 188
 189        n810_jack_func = ucontrol->value.integer.value[0];
 190        n810_ext_control(codec);
 191
 192        return 1;
 193}
 194
 195static int n810_get_input(struct snd_kcontrol *kcontrol,
 196                          struct snd_ctl_elem_value *ucontrol)
 197{
 198        ucontrol->value.integer.value[0] = n810_dmic_func;
 199
 200        return 0;
 201}
 202
 203static int n810_set_input(struct snd_kcontrol *kcontrol,
 204                          struct snd_ctl_elem_value *ucontrol)
 205{
 206        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 207
 208        if (n810_dmic_func == ucontrol->value.integer.value[0])
 209                return 0;
 210
 211        n810_dmic_func = ucontrol->value.integer.value[0];
 212        n810_ext_control(codec);
 213
 214        return 1;
 215}
 216
 217static int n810_spk_event(struct snd_soc_dapm_widget *w,
 218                          struct snd_kcontrol *k, int event)
 219{
 220        if (SND_SOC_DAPM_EVENT_ON(event))
 221                gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
 222        else
 223                gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
 224
 225        return 0;
 226}
 227
 228static int n810_jack_event(struct snd_soc_dapm_widget *w,
 229                           struct snd_kcontrol *k, int event)
 230{
 231        if (SND_SOC_DAPM_EVENT_ON(event))
 232                gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
 233        else
 234                gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
 235
 236        return 0;
 237}
 238
 239static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
 240        SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
 241        SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
 242        SND_SOC_DAPM_MIC("DMic", NULL),
 243};
 244
 245static const struct snd_soc_dapm_route audio_map[] = {
 246        {"Headphone Jack", NULL, "HPLOUT"},
 247        {"Headphone Jack", NULL, "HPROUT"},
 248
 249        {"Ext Spk", NULL, "LLOUT"},
 250        {"Ext Spk", NULL, "RLOUT"},
 251
 252        {"DMic Rate 64", NULL, "Mic Bias 2V"},
 253        {"Mic Bias 2V", NULL, "DMic"},
 254};
 255
 256static const char *spk_function[] = {"Off", "On"};
 257static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
 258static const char *input_function[] = {"ADC", "Digital Mic"};
 259static const struct soc_enum n810_enum[] = {
 260        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
 261        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
 262        SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
 263};
 264
 265static const struct snd_kcontrol_new aic33_n810_controls[] = {
 266        SOC_ENUM_EXT("Speaker Function", n810_enum[0],
 267                     n810_get_spk, n810_set_spk),
 268        SOC_ENUM_EXT("Jack Function", n810_enum[1],
 269                     n810_get_jack, n810_set_jack),
 270        SOC_ENUM_EXT("Input Select",  n810_enum[2],
 271                     n810_get_input, n810_set_input),
 272};
 273
 274static int n810_aic33_init(struct snd_soc_codec *codec)
 275{
 276        int err;
 277
 278        /* Not connected */
 279        snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
 280        snd_soc_dapm_nc_pin(codec, "HPLCOM");
 281        snd_soc_dapm_nc_pin(codec, "HPRCOM");
 282        snd_soc_dapm_nc_pin(codec, "MIC3L");
 283        snd_soc_dapm_nc_pin(codec, "MIC3R");
 284        snd_soc_dapm_nc_pin(codec, "LINE1R");
 285        snd_soc_dapm_nc_pin(codec, "LINE2L");
 286        snd_soc_dapm_nc_pin(codec, "LINE2R");
 287
 288        /* Add N810 specific controls */
 289        err = snd_soc_add_controls(codec, aic33_n810_controls,
 290                                ARRAY_SIZE(aic33_n810_controls));
 291        if (err < 0)
 292                return err;
 293
 294        /* Add N810 specific widgets */
 295        snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,
 296                                  ARRAY_SIZE(aic33_dapm_widgets));
 297
 298        /* Set up N810 specific audio path audio_map */
 299        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 300
 301        snd_soc_dapm_sync(codec);
 302
 303        return 0;
 304}
 305
 306/* Digital audio interface glue - connects codec <--> CPU */
 307static struct snd_soc_dai_link n810_dai = {
 308        .name = "TLV320AIC33",
 309        .stream_name = "AIC33",
 310        .cpu_dai = &omap_mcbsp_dai[0],
 311        .codec_dai = &aic3x_dai,
 312        .init = n810_aic33_init,
 313        .ops = &n810_ops,
 314};
 315
 316/* Audio machine driver */
 317static struct snd_soc_card snd_soc_n810 = {
 318        .name = "N810",
 319        .platform = &omap_soc_platform,
 320        .dai_link = &n810_dai,
 321        .num_links = 1,
 322};
 323
 324/* Audio private data */
 325static struct aic3x_setup_data n810_aic33_setup = {
 326        .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,
 327        .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT,
 328};
 329
 330/* Audio subsystem */
 331static struct snd_soc_device n810_snd_devdata = {
 332        .card = &snd_soc_n810,
 333        .codec_dev = &soc_codec_dev_aic3x,
 334        .codec_data = &n810_aic33_setup,
 335};
 336
 337static struct platform_device *n810_snd_device;
 338
 339/* temporary i2c device creation until this can be moved into the machine
 340 * support file.
 341*/
 342static struct i2c_board_info i2c_device[] = {
 343        { I2C_BOARD_INFO("tlv320aic3x", 0x1b), }
 344};
 345
 346static int __init n810_soc_init(void)
 347{
 348        int err;
 349        struct device *dev;
 350
 351        if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
 352                return -ENODEV;
 353
 354        i2c_register_board_info(1, i2c_device, ARRAY_SIZE(i2c_device));
 355
 356        n810_snd_device = platform_device_alloc("soc-audio", -1);
 357        if (!n810_snd_device)
 358                return -ENOMEM;
 359
 360        platform_set_drvdata(n810_snd_device, &n810_snd_devdata);
 361        n810_snd_devdata.dev = &n810_snd_device->dev;
 362        *(unsigned int *)n810_dai.cpu_dai->private_data = 1; /* McBSP2 */
 363        err = platform_device_add(n810_snd_device);
 364        if (err)
 365                goto err1;
 366
 367        dev = &n810_snd_device->dev;
 368
 369        sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
 370        if (IS_ERR(sys_clkout2_src)) {
 371                dev_err(dev, "Could not get sys_clkout2_src clock\n");
 372                err = PTR_ERR(sys_clkout2_src);
 373                goto err2;
 374        }
 375        sys_clkout2 = clk_get(dev, "sys_clkout2");
 376        if (IS_ERR(sys_clkout2)) {
 377                dev_err(dev, "Could not get sys_clkout2\n");
 378                err = PTR_ERR(sys_clkout2);
 379                goto err3;
 380        }
 381        /*
 382         * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
 383         * 96 MHz as its parent in order to get 12 MHz
 384         */
 385        func96m_clk = clk_get(dev, "func_96m_ck");
 386        if (IS_ERR(func96m_clk)) {
 387                dev_err(dev, "Could not get func 96M clock\n");
 388                err = PTR_ERR(func96m_clk);
 389                goto err4;
 390        }
 391        clk_set_parent(sys_clkout2_src, func96m_clk);
 392        clk_set_rate(sys_clkout2, 12000000);
 393
 394        BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
 395               (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0));
 396
 397        gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
 398        gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 399
 400        return 0;
 401err4:
 402        clk_put(sys_clkout2);
 403err3:
 404        clk_put(sys_clkout2_src);
 405err2:
 406        platform_device_del(n810_snd_device);
 407err1:
 408        platform_device_put(n810_snd_device);
 409
 410        return err;
 411}
 412
 413static void __exit n810_soc_exit(void)
 414{
 415        gpio_free(N810_SPEAKER_AMP_GPIO);
 416        gpio_free(N810_HEADSET_AMP_GPIO);
 417        clk_put(sys_clkout2_src);
 418        clk_put(sys_clkout2);
 419        clk_put(func96m_clk);
 420
 421        platform_device_unregister(n810_snd_device);
 422}
 423
 424module_init(n810_soc_init);
 425module_exit(n810_soc_exit);
 426
 427MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
 428MODULE_DESCRIPTION("ALSA SoC Nokia N810");
 429MODULE_LICENSE("GPL");
 430