linux/sound/soc/tegra/tegra_wm8903.c
<<
>>
Prefs
   1/*
   2 * tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
   3 *
   4 * Author: Stephen Warren <swarren@nvidia.com>
   5 * Copyright (C) 2010-2011 - NVIDIA, Inc.
   6 *
   7 * Based on code copyright/by:
   8 *
   9 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
  10 *
  11 * Copyright 2007 Wolfson Microelectronics PLC.
  12 * Author: Graeme Gregory
  13 *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
  14 *
  15 * This program is free software; you can redistribute it and/or
  16 * modify it under the terms of the GNU General Public License
  17 * version 2 as published by the Free Software Foundation.
  18 *
  19 * This program is distributed in the hope that it will be useful, but
  20 * WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  22 * General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  27 * 02110-1301 USA
  28 *
  29 */
  30
  31#include <asm/mach-types.h>
  32
  33#include <linux/module.h>
  34#include <linux/platform_device.h>
  35#include <linux/slab.h>
  36#include <linux/gpio.h>
  37#include <linux/of_gpio.h>
  38
  39#include <mach/tegra_wm8903_pdata.h>
  40
  41#include <sound/core.h>
  42#include <sound/jack.h>
  43#include <sound/pcm.h>
  44#include <sound/pcm_params.h>
  45#include <sound/soc.h>
  46
  47#include "../codecs/wm8903.h"
  48
  49#include "tegra_das.h"
  50#include "tegra_i2s.h"
  51#include "tegra_pcm.h"
  52#include "tegra_asoc_utils.h"
  53
  54#define DRV_NAME "tegra-snd-wm8903"
  55
  56#define GPIO_SPKR_EN    BIT(0)
  57#define GPIO_HP_MUTE    BIT(1)
  58#define GPIO_INT_MIC_EN BIT(2)
  59#define GPIO_EXT_MIC_EN BIT(3)
  60#define GPIO_HP_DET     BIT(4)
  61
  62struct tegra_wm8903 {
  63        struct tegra_wm8903_platform_data pdata;
  64        struct platform_device *pcm_dev;
  65        struct tegra_asoc_utils_data util_data;
  66        int gpio_requested;
  67};
  68
  69static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
  70                                        struct snd_pcm_hw_params *params)
  71{
  72        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  73        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  74        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  75        struct snd_soc_codec *codec = rtd->codec;
  76        struct snd_soc_card *card = codec->card;
  77        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
  78        int srate, mclk;
  79        int err;
  80
  81        srate = params_rate(params);
  82        switch (srate) {
  83        case 64000:
  84        case 88200:
  85        case 96000:
  86                mclk = 128 * srate;
  87                break;
  88        default:
  89                mclk = 256 * srate;
  90                break;
  91        }
  92        /* FIXME: Codec only requires >= 3MHz if OSR==0 */
  93        while (mclk < 6000000)
  94                mclk *= 2;
  95
  96        err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
  97        if (err < 0) {
  98                dev_err(card->dev, "Can't configure clocks\n");
  99                return err;
 100        }
 101
 102        err = snd_soc_dai_set_fmt(codec_dai,
 103                                        SND_SOC_DAIFMT_I2S |
 104                                        SND_SOC_DAIFMT_NB_NF |
 105                                        SND_SOC_DAIFMT_CBS_CFS);
 106        if (err < 0) {
 107                dev_err(card->dev, "codec_dai fmt not set\n");
 108                return err;
 109        }
 110
 111        err = snd_soc_dai_set_fmt(cpu_dai,
 112                                        SND_SOC_DAIFMT_I2S |
 113                                        SND_SOC_DAIFMT_NB_NF |
 114                                        SND_SOC_DAIFMT_CBS_CFS);
 115        if (err < 0) {
 116                dev_err(card->dev, "cpu_dai fmt not set\n");
 117                return err;
 118        }
 119
 120        err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
 121                                        SND_SOC_CLOCK_IN);
 122        if (err < 0) {
 123                dev_err(card->dev, "codec_dai clock not set\n");
 124                return err;
 125        }
 126
 127        return 0;
 128}
 129
 130static struct snd_soc_ops tegra_wm8903_ops = {
 131        .hw_params = tegra_wm8903_hw_params,
 132};
 133
 134static struct snd_soc_jack tegra_wm8903_hp_jack;
 135
 136static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
 137        {
 138                .pin = "Headphone Jack",
 139                .mask = SND_JACK_HEADPHONE,
 140        },
 141};
 142
 143static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
 144        .name = "headphone detect",
 145        .report = SND_JACK_HEADPHONE,
 146        .debounce_time = 150,
 147        .invert = 1,
 148};
 149
 150static struct snd_soc_jack tegra_wm8903_mic_jack;
 151
 152static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
 153        {
 154                .pin = "Mic Jack",
 155                .mask = SND_JACK_MICROPHONE,
 156        },
 157};
 158
 159static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
 160                                        struct snd_kcontrol *k, int event)
 161{
 162        struct snd_soc_dapm_context *dapm = w->dapm;
 163        struct snd_soc_card *card = dapm->card;
 164        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 165        struct tegra_wm8903_platform_data *pdata = &machine->pdata;
 166
 167        if (!(machine->gpio_requested & GPIO_SPKR_EN))
 168                return 0;
 169
 170        gpio_set_value_cansleep(pdata->gpio_spkr_en,
 171                                SND_SOC_DAPM_EVENT_ON(event));
 172
 173        return 0;
 174}
 175
 176static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
 177                                        struct snd_kcontrol *k, int event)
 178{
 179        struct snd_soc_dapm_context *dapm = w->dapm;
 180        struct snd_soc_card *card = dapm->card;
 181        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 182        struct tegra_wm8903_platform_data *pdata = &machine->pdata;
 183
 184        if (!(machine->gpio_requested & GPIO_HP_MUTE))
 185                return 0;
 186
 187        gpio_set_value_cansleep(pdata->gpio_hp_mute,
 188                                !SND_SOC_DAPM_EVENT_ON(event));
 189
 190        return 0;
 191}
 192
 193static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
 194        SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
 195        SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
 196        SND_SOC_DAPM_MIC("Mic Jack", NULL),
 197};
 198
 199static const struct snd_soc_dapm_route harmony_audio_map[] = {
 200        {"Headphone Jack", NULL, "HPOUTR"},
 201        {"Headphone Jack", NULL, "HPOUTL"},
 202        {"Int Spk", NULL, "ROP"},
 203        {"Int Spk", NULL, "RON"},
 204        {"Int Spk", NULL, "LOP"},
 205        {"Int Spk", NULL, "LON"},
 206        {"Mic Jack", NULL, "MICBIAS"},
 207        {"IN1L", NULL, "Mic Jack"},
 208};
 209
 210static const struct snd_soc_dapm_route seaboard_audio_map[] = {
 211        {"Headphone Jack", NULL, "HPOUTR"},
 212        {"Headphone Jack", NULL, "HPOUTL"},
 213        {"Int Spk", NULL, "ROP"},
 214        {"Int Spk", NULL, "RON"},
 215        {"Int Spk", NULL, "LOP"},
 216        {"Int Spk", NULL, "LON"},
 217        {"Mic Jack", NULL, "MICBIAS"},
 218        {"IN1R", NULL, "Mic Jack"},
 219};
 220
 221static const struct snd_soc_dapm_route kaen_audio_map[] = {
 222        {"Headphone Jack", NULL, "HPOUTR"},
 223        {"Headphone Jack", NULL, "HPOUTL"},
 224        {"Int Spk", NULL, "ROP"},
 225        {"Int Spk", NULL, "RON"},
 226        {"Int Spk", NULL, "LOP"},
 227        {"Int Spk", NULL, "LON"},
 228        {"Mic Jack", NULL, "MICBIAS"},
 229        {"IN2R", NULL, "Mic Jack"},
 230};
 231
 232static const struct snd_soc_dapm_route aebl_audio_map[] = {
 233        {"Headphone Jack", NULL, "HPOUTR"},
 234        {"Headphone Jack", NULL, "HPOUTL"},
 235        {"Int Spk", NULL, "LINEOUTR"},
 236        {"Int Spk", NULL, "LINEOUTL"},
 237        {"Mic Jack", NULL, "MICBIAS"},
 238        {"IN1R", NULL, "Mic Jack"},
 239};
 240
 241static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
 242        SOC_DAPM_PIN_SWITCH("Int Spk"),
 243};
 244
 245static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
 246{
 247        struct snd_soc_codec *codec = rtd->codec;
 248        struct snd_soc_dapm_context *dapm = &codec->dapm;
 249        struct snd_soc_card *card = codec->card;
 250        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 251        struct tegra_wm8903_platform_data *pdata = &machine->pdata;
 252        struct device_node *np = card->dev->of_node;
 253        int ret;
 254
 255        if (card->dev->platform_data) {
 256                memcpy(pdata, card->dev->platform_data, sizeof(*pdata));
 257        } else if (np) {
 258                /*
 259                 * This part must be in init() rather than probe() in order to
 260                 * guarantee that the WM8903 has been probed, and hence its
 261                 * GPIO controller registered, which is a pre-condition for
 262                 * of_get_named_gpio() to be able to map the phandles in the
 263                 * properties to the controller node. Given this, all
 264                 * pdata handling is in init() for consistency.
 265                 */
 266                pdata->gpio_spkr_en = of_get_named_gpio(np,
 267                                                "nvidia,spkr-en-gpios", 0);
 268                pdata->gpio_hp_mute = of_get_named_gpio(np,
 269                                                "nvidia,hp-mute-gpios", 0);
 270                pdata->gpio_hp_det = of_get_named_gpio(np,
 271                                                "nvidia,hp-det-gpios", 0);
 272                pdata->gpio_int_mic_en = of_get_named_gpio(np,
 273                                                "nvidia,int-mic-en-gpios", 0);
 274                pdata->gpio_ext_mic_en = of_get_named_gpio(np,
 275                                                "nvidia,ext-mic-en-gpios", 0);
 276        } else {
 277                dev_err(card->dev, "No platform data supplied\n");
 278                return -EINVAL;
 279        }
 280
 281        if (gpio_is_valid(pdata->gpio_spkr_en)) {
 282                ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
 283                if (ret) {
 284                        dev_err(card->dev, "cannot get spkr_en gpio\n");
 285                        return ret;
 286                }
 287                machine->gpio_requested |= GPIO_SPKR_EN;
 288
 289                gpio_direction_output(pdata->gpio_spkr_en, 0);
 290        }
 291
 292        if (gpio_is_valid(pdata->gpio_hp_mute)) {
 293                ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
 294                if (ret) {
 295                        dev_err(card->dev, "cannot get hp_mute gpio\n");
 296                        return ret;
 297                }
 298                machine->gpio_requested |= GPIO_HP_MUTE;
 299
 300                gpio_direction_output(pdata->gpio_hp_mute, 1);
 301        }
 302
 303        if (gpio_is_valid(pdata->gpio_int_mic_en)) {
 304                ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
 305                if (ret) {
 306                        dev_err(card->dev, "cannot get int_mic_en gpio\n");
 307                        return ret;
 308                }
 309                machine->gpio_requested |= GPIO_INT_MIC_EN;
 310
 311                /* Disable int mic; enable signal is active-high */
 312                gpio_direction_output(pdata->gpio_int_mic_en, 0);
 313        }
 314
 315        if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
 316                ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
 317                if (ret) {
 318                        dev_err(card->dev, "cannot get ext_mic_en gpio\n");
 319                        return ret;
 320                }
 321                machine->gpio_requested |= GPIO_EXT_MIC_EN;
 322
 323                /* Enable ext mic; enable signal is active-low */
 324                gpio_direction_output(pdata->gpio_ext_mic_en, 0);
 325        }
 326
 327        if (gpio_is_valid(pdata->gpio_hp_det)) {
 328                tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
 329                snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
 330                                &tegra_wm8903_hp_jack);
 331                snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
 332                                        ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
 333                                        tegra_wm8903_hp_jack_pins);
 334                snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
 335                                        1,
 336                                        &tegra_wm8903_hp_jack_gpio);
 337                machine->gpio_requested |= GPIO_HP_DET;
 338        }
 339
 340        snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
 341                         &tegra_wm8903_mic_jack);
 342        snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
 343                              ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
 344                              tegra_wm8903_mic_jack_pins);
 345        wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
 346                                0);
 347
 348        snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
 349
 350        return 0;
 351}
 352
 353static struct snd_soc_dai_link tegra_wm8903_dai = {
 354        .name = "WM8903",
 355        .stream_name = "WM8903 PCM",
 356        .codec_name = "wm8903.0-001a",
 357        .platform_name = "tegra-pcm-audio",
 358        .cpu_dai_name = "tegra-i2s.0",
 359        .codec_dai_name = "wm8903-hifi",
 360        .init = tegra_wm8903_init,
 361        .ops = &tegra_wm8903_ops,
 362};
 363
 364static struct snd_soc_card snd_soc_tegra_wm8903 = {
 365        .name = "tegra-wm8903",
 366        .owner = THIS_MODULE,
 367        .dai_link = &tegra_wm8903_dai,
 368        .num_links = 1,
 369
 370        .controls = tegra_wm8903_controls,
 371        .num_controls = ARRAY_SIZE(tegra_wm8903_controls),
 372        .dapm_widgets = tegra_wm8903_dapm_widgets,
 373        .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
 374        .fully_routed = true,
 375};
 376
 377static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
 378{
 379        struct snd_soc_card *card = &snd_soc_tegra_wm8903;
 380        struct tegra_wm8903 *machine;
 381        int ret;
 382
 383        if (!pdev->dev.platform_data && !pdev->dev.of_node) {
 384                dev_err(&pdev->dev, "No platform data supplied\n");
 385                return -EINVAL;
 386        }
 387
 388        machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
 389                               GFP_KERNEL);
 390        if (!machine) {
 391                dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
 392                ret = -ENOMEM;
 393                goto err;
 394        }
 395        machine->pcm_dev = ERR_PTR(-EINVAL);
 396
 397        card->dev = &pdev->dev;
 398        platform_set_drvdata(pdev, card);
 399        snd_soc_card_set_drvdata(card, machine);
 400
 401        if (pdev->dev.of_node) {
 402                ret = snd_soc_of_parse_card_name(card, "nvidia,model");
 403                if (ret)
 404                        goto err;
 405
 406                ret = snd_soc_of_parse_audio_routing(card,
 407                                                     "nvidia,audio-routing");
 408                if (ret)
 409                        goto err;
 410
 411                tegra_wm8903_dai.codec_name = NULL;
 412                tegra_wm8903_dai.codec_of_node = of_parse_phandle(
 413                                pdev->dev.of_node, "nvidia,audio-codec", 0);
 414                if (!tegra_wm8903_dai.codec_of_node) {
 415                        dev_err(&pdev->dev,
 416                                "Property 'nvidia,audio-codec' missing or invalid\n");
 417                        ret = -EINVAL;
 418                        goto err;
 419                }
 420
 421                tegra_wm8903_dai.cpu_dai_name = NULL;
 422                tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle(
 423                                pdev->dev.of_node, "nvidia,i2s-controller", 0);
 424                if (!tegra_wm8903_dai.cpu_dai_of_node) {
 425                        dev_err(&pdev->dev,
 426                                "Property 'nvidia,i2s-controller' missing or invalid\n");
 427                        ret = -EINVAL;
 428                        goto err;
 429                }
 430
 431                machine->pcm_dev = platform_device_register_simple(
 432                                        "tegra-pcm-audio", -1, NULL, 0);
 433                if (IS_ERR(machine->pcm_dev)) {
 434                        dev_err(&pdev->dev,
 435                                "Can't instantiate tegra-pcm-audio\n");
 436                        ret = PTR_ERR(machine->pcm_dev);
 437                        goto err;
 438                }
 439        } else {
 440                if (machine_is_harmony()) {
 441                        card->dapm_routes = harmony_audio_map;
 442                        card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
 443                } else if (machine_is_seaboard()) {
 444                        card->dapm_routes = seaboard_audio_map;
 445                        card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
 446                } else if (machine_is_kaen()) {
 447                        card->dapm_routes = kaen_audio_map;
 448                        card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
 449                } else {
 450                        card->dapm_routes = aebl_audio_map;
 451                        card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
 452                }
 453        }
 454
 455        ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
 456        if (ret)
 457                goto err_unregister;
 458
 459        ret = snd_soc_register_card(card);
 460        if (ret) {
 461                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
 462                        ret);
 463                goto err_fini_utils;
 464        }
 465
 466        return 0;
 467
 468err_fini_utils:
 469        tegra_asoc_utils_fini(&machine->util_data);
 470err_unregister:
 471        if (!IS_ERR(machine->pcm_dev))
 472                platform_device_unregister(machine->pcm_dev);
 473err:
 474        return ret;
 475}
 476
 477static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
 478{
 479        struct snd_soc_card *card = platform_get_drvdata(pdev);
 480        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 481        struct tegra_wm8903_platform_data *pdata = &machine->pdata;
 482
 483        if (machine->gpio_requested & GPIO_HP_DET)
 484                snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack,
 485                                        1,
 486                                        &tegra_wm8903_hp_jack_gpio);
 487        if (machine->gpio_requested & GPIO_EXT_MIC_EN)
 488                gpio_free(pdata->gpio_ext_mic_en);
 489        if (machine->gpio_requested & GPIO_INT_MIC_EN)
 490                gpio_free(pdata->gpio_int_mic_en);
 491        if (machine->gpio_requested & GPIO_HP_MUTE)
 492                gpio_free(pdata->gpio_hp_mute);
 493        if (machine->gpio_requested & GPIO_SPKR_EN)
 494                gpio_free(pdata->gpio_spkr_en);
 495        machine->gpio_requested = 0;
 496
 497        snd_soc_unregister_card(card);
 498
 499        tegra_asoc_utils_fini(&machine->util_data);
 500        if (!IS_ERR(machine->pcm_dev))
 501                platform_device_unregister(machine->pcm_dev);
 502
 503        return 0;
 504}
 505
 506static const struct of_device_id tegra_wm8903_of_match[] __devinitconst = {
 507        { .compatible = "nvidia,tegra-audio-wm8903", },
 508        {},
 509};
 510
 511static struct platform_driver tegra_wm8903_driver = {
 512        .driver = {
 513                .name = DRV_NAME,
 514                .owner = THIS_MODULE,
 515                .pm = &snd_soc_pm_ops,
 516                .of_match_table = tegra_wm8903_of_match,
 517        },
 518        .probe = tegra_wm8903_driver_probe,
 519        .remove = __devexit_p(tegra_wm8903_driver_remove),
 520};
 521module_platform_driver(tegra_wm8903_driver);
 522
 523MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 524MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
 525MODULE_LICENSE("GPL");
 526MODULE_ALIAS("platform:" DRV_NAME);
 527MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match);
 528
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.