linux/sound/soc/pxa/tosa.c
<<
>>
Prefs
   1/*
   2 * tosa.c  --  SoC audio for Tosa
   3 *
   4 * Copyright 2005 Wolfson Microelectronics PLC.
   5 * Copyright 2005 Openedhand Ltd.
   6 *
   7 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
   8 *          Richard Purdie <richard@openedhand.com>
   9 *
  10 *  This program is free software; you can redistribute  it and/or modify it
  11 *  under  the terms of  the GNU General  Public License as published by the
  12 *  Free Software Foundation;  either version 2 of the  License, or (at your
  13 *  option) any later version.
  14 *
  15 * GPIO's
  16 *  1 - Jack Insertion
  17 *  5 - Hookswitch (headset answer/hang up switch)
  18 *
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/moduleparam.h>
  23#include <linux/device.h>
  24#include <linux/gpio.h>
  25
  26#include <sound/core.h>
  27#include <sound/pcm.h>
  28#include <sound/soc.h>
  29
  30#include <asm/mach-types.h>
  31#include <mach/tosa.h>
  32#include <mach/audio.h>
  33
  34#include "../codecs/wm9712.h"
  35#include "pxa2xx-ac97.h"
  36
  37#define TOSA_HP        0
  38#define TOSA_MIC_INT   1
  39#define TOSA_HEADSET   2
  40#define TOSA_HP_OFF    3
  41#define TOSA_SPK_ON    0
  42#define TOSA_SPK_OFF   1
  43
  44static int tosa_jack_func;
  45static int tosa_spk_func;
  46
  47static void tosa_ext_control(struct snd_soc_dapm_context *dapm)
  48{
  49
  50        snd_soc_dapm_mutex_lock(dapm);
  51
  52        /* set up jack connection */
  53        switch (tosa_jack_func) {
  54        case TOSA_HP:
  55                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
  56                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  57                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  58                break;
  59        case TOSA_MIC_INT:
  60                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic (Internal)");
  61                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  62                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  63                break;
  64        case TOSA_HEADSET:
  65                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic (Internal)");
  66                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  67                snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
  68                break;
  69        }
  70
  71        if (tosa_spk_func == TOSA_SPK_ON)
  72                snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
  73        else
  74                snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
  75
  76        snd_soc_dapm_sync_unlocked(dapm);
  77
  78        snd_soc_dapm_mutex_unlock(dapm);
  79}
  80
  81static int tosa_startup(struct snd_pcm_substream *substream)
  82{
  83        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  84
  85        /* check the jack status at stream startup */
  86        tosa_ext_control(&rtd->card->dapm);
  87
  88        return 0;
  89}
  90
  91static struct snd_soc_ops tosa_ops = {
  92        .startup = tosa_startup,
  93};
  94
  95static int tosa_get_jack(struct snd_kcontrol *kcontrol,
  96        struct snd_ctl_elem_value *ucontrol)
  97{
  98        ucontrol->value.enumerated.item[0] = tosa_jack_func;
  99        return 0;
 100}
 101
 102static int tosa_set_jack(struct snd_kcontrol *kcontrol,
 103        struct snd_ctl_elem_value *ucontrol)
 104{
 105        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 106
 107        if (tosa_jack_func == ucontrol->value.enumerated.item[0])
 108                return 0;
 109
 110        tosa_jack_func = ucontrol->value.enumerated.item[0];
 111        tosa_ext_control(&card->dapm);
 112        return 1;
 113}
 114
 115static int tosa_get_spk(struct snd_kcontrol *kcontrol,
 116        struct snd_ctl_elem_value *ucontrol)
 117{
 118        ucontrol->value.enumerated.item[0] = tosa_spk_func;
 119        return 0;
 120}
 121
 122static int tosa_set_spk(struct snd_kcontrol *kcontrol,
 123        struct snd_ctl_elem_value *ucontrol)
 124{
 125        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 126
 127        if (tosa_spk_func == ucontrol->value.enumerated.item[0])
 128                return 0;
 129
 130        tosa_spk_func = ucontrol->value.enumerated.item[0];
 131        tosa_ext_control(&card->dapm);
 132        return 1;
 133}
 134
 135/* tosa dapm event handlers */
 136static int tosa_hp_event(struct snd_soc_dapm_widget *w,
 137        struct snd_kcontrol *k, int event)
 138{
 139        gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
 140        return 0;
 141}
 142
 143/* tosa machine dapm widgets */
 144static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
 145SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
 146SND_SOC_DAPM_HP("Headset Jack", NULL),
 147SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
 148SND_SOC_DAPM_SPK("Speaker", NULL),
 149};
 150
 151/* tosa audio map */
 152static const struct snd_soc_dapm_route audio_map[] = {
 153
 154        /* headphone connected to HPOUTL, HPOUTR */
 155        {"Headphone Jack", NULL, "HPOUTL"},
 156        {"Headphone Jack", NULL, "HPOUTR"},
 157
 158        /* ext speaker connected to LOUT2, ROUT2 */
 159        {"Speaker", NULL, "LOUT2"},
 160        {"Speaker", NULL, "ROUT2"},
 161
 162        /* internal mic is connected to mic1, mic2 differential - with bias */
 163        {"MIC1", NULL, "Mic Bias"},
 164        {"MIC2", NULL, "Mic Bias"},
 165        {"Mic Bias", NULL, "Mic (Internal)"},
 166
 167        /* headset is connected to HPOUTR, and LINEINR with bias */
 168        {"Headset Jack", NULL, "HPOUTR"},
 169        {"LINEINR", NULL, "Mic Bias"},
 170        {"Mic Bias", NULL, "Headset Jack"},
 171};
 172
 173static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
 174        "Off"};
 175static const char *spk_function[] = {"On", "Off"};
 176static const struct soc_enum tosa_enum[] = {
 177        SOC_ENUM_SINGLE_EXT(5, jack_function),
 178        SOC_ENUM_SINGLE_EXT(2, spk_function),
 179};
 180
 181static const struct snd_kcontrol_new tosa_controls[] = {
 182        SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
 183                tosa_set_jack),
 184        SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
 185                tosa_set_spk),
 186};
 187
 188static struct snd_soc_dai_link tosa_dai[] = {
 189{
 190        .name = "AC97",
 191        .stream_name = "AC97 HiFi",
 192        .cpu_dai_name = "pxa2xx-ac97",
 193        .codec_dai_name = "wm9712-hifi",
 194        .platform_name = "pxa-pcm-audio",
 195        .codec_name = "wm9712-codec",
 196        .ops = &tosa_ops,
 197},
 198{
 199        .name = "AC97 Aux",
 200        .stream_name = "AC97 Aux",
 201        .cpu_dai_name = "pxa2xx-ac97-aux",
 202        .codec_dai_name = "wm9712-aux",
 203        .platform_name = "pxa-pcm-audio",
 204        .codec_name = "wm9712-codec",
 205        .ops = &tosa_ops,
 206},
 207};
 208
 209static struct snd_soc_card tosa = {
 210        .name = "Tosa",
 211        .owner = THIS_MODULE,
 212        .dai_link = tosa_dai,
 213        .num_links = ARRAY_SIZE(tosa_dai),
 214
 215        .controls = tosa_controls,
 216        .num_controls = ARRAY_SIZE(tosa_controls),
 217        .dapm_widgets = tosa_dapm_widgets,
 218        .num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
 219        .dapm_routes = audio_map,
 220        .num_dapm_routes = ARRAY_SIZE(audio_map),
 221        .fully_routed = true,
 222};
 223
 224static int tosa_probe(struct platform_device *pdev)
 225{
 226        struct snd_soc_card *card = &tosa;
 227        int ret;
 228
 229        ret = gpio_request_one(TOSA_GPIO_L_MUTE, GPIOF_OUT_INIT_LOW,
 230                               "Headphone Jack");
 231        if (ret)
 232                return ret;
 233
 234        card->dev = &pdev->dev;
 235
 236        ret = devm_snd_soc_register_card(&pdev->dev, card);
 237        if (ret) {
 238                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
 239                        ret);
 240                gpio_free(TOSA_GPIO_L_MUTE);
 241        }
 242        return ret;
 243}
 244
 245static int tosa_remove(struct platform_device *pdev)
 246{
 247        gpio_free(TOSA_GPIO_L_MUTE);
 248        return 0;
 249}
 250
 251static struct platform_driver tosa_driver = {
 252        .driver         = {
 253                .name   = "tosa-audio",
 254                .pm     = &snd_soc_pm_ops,
 255        },
 256        .probe          = tosa_probe,
 257        .remove         = tosa_remove,
 258};
 259
 260module_platform_driver(tosa_driver);
 261
 262/* Module information */
 263MODULE_AUTHOR("Richard Purdie");
 264MODULE_DESCRIPTION("ALSA SoC Tosa");
 265MODULE_LICENSE("GPL");
 266MODULE_ALIAS("platform:tosa-audio");
 267