linux/sound/soc/pxa/spitz.c
<<
>>
Prefs
   1/*
   2 * spitz.c  --  SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
   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 */
  16
  17#include <linux/module.h>
  18#include <linux/moduleparam.h>
  19#include <linux/timer.h>
  20#include <linux/interrupt.h>
  21#include <linux/platform_device.h>
  22#include <linux/gpio.h>
  23#include <sound/core.h>
  24#include <sound/pcm.h>
  25#include <sound/soc.h>
  26
  27#include <asm/mach-types.h>
  28#include <mach/spitz.h>
  29#include "../codecs/wm8750.h"
  30#include "pxa2xx-i2s.h"
  31
  32#define SPITZ_HP        0
  33#define SPITZ_MIC       1
  34#define SPITZ_LINE      2
  35#define SPITZ_HEADSET   3
  36#define SPITZ_HP_OFF    4
  37#define SPITZ_SPK_ON    0
  38#define SPITZ_SPK_OFF   1
  39
  40 /* audio clock in Hz - rounded from 12.235MHz */
  41#define SPITZ_AUDIO_CLOCK 12288000
  42
  43static int spitz_jack_func;
  44static int spitz_spk_func;
  45static int spitz_mic_gpio;
  46
  47static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
  48{
  49        snd_soc_dapm_mutex_lock(dapm);
  50
  51        if (spitz_spk_func == SPITZ_SPK_ON)
  52                snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
  53        else
  54                snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
  55
  56        /* set up jack connection */
  57        switch (spitz_jack_func) {
  58        case SPITZ_HP:
  59                /* enable and unmute hp jack, disable mic bias */
  60                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  61                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  62                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  63                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  64                gpio_set_value(SPITZ_GPIO_MUTE_L, 1);
  65                gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
  66                break;
  67        case SPITZ_MIC:
  68                /* enable mic jack and bias, mute hp */
  69                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  70                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  71                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  72                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  73                gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
  74                gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
  75                break;
  76        case SPITZ_LINE:
  77                /* enable line jack, disable mic bias and mute hp */
  78                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  79                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  80                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  81                snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
  82                gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
  83                gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
  84                break;
  85        case SPITZ_HEADSET:
  86                /* enable and unmute headset jack enable mic bias, mute L hp */
  87                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  88                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  89                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  90                snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
  91                gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
  92                gpio_set_value(SPITZ_GPIO_MUTE_R, 1);
  93                break;
  94        case SPITZ_HP_OFF:
  95
  96                /* jack removed, everything off */
  97                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  98                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  99                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
 100                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
 101                gpio_set_value(SPITZ_GPIO_MUTE_L, 0);
 102                gpio_set_value(SPITZ_GPIO_MUTE_R, 0);
 103                break;
 104        }
 105
 106        snd_soc_dapm_sync_unlocked(dapm);
 107
 108        snd_soc_dapm_mutex_unlock(dapm);
 109}
 110
 111static int spitz_startup(struct snd_pcm_substream *substream)
 112{
 113        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 114
 115        /* check the jack status at stream startup */
 116        spitz_ext_control(&rtd->card->dapm);
 117
 118        return 0;
 119}
 120
 121static int spitz_hw_params(struct snd_pcm_substream *substream,
 122        struct snd_pcm_hw_params *params)
 123{
 124        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 125        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 126        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 127        unsigned int clk = 0;
 128        int ret = 0;
 129
 130        switch (params_rate(params)) {
 131        case 8000:
 132        case 16000:
 133        case 48000:
 134        case 96000:
 135                clk = 12288000;
 136                break;
 137        case 11025:
 138        case 22050:
 139        case 44100:
 140                clk = 11289600;
 141                break;
 142        }
 143
 144        /* set the codec system clock for DAC and ADC */
 145        ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
 146                SND_SOC_CLOCK_IN);
 147        if (ret < 0)
 148                return ret;
 149
 150        /* set the I2S system clock as input (unused) */
 151        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 152                SND_SOC_CLOCK_IN);
 153        if (ret < 0)
 154                return ret;
 155
 156        return 0;
 157}
 158
 159static struct snd_soc_ops spitz_ops = {
 160        .startup = spitz_startup,
 161        .hw_params = spitz_hw_params,
 162};
 163
 164static int spitz_get_jack(struct snd_kcontrol *kcontrol,
 165        struct snd_ctl_elem_value *ucontrol)
 166{
 167        ucontrol->value.integer.value[0] = spitz_jack_func;
 168        return 0;
 169}
 170
 171static int spitz_set_jack(struct snd_kcontrol *kcontrol,
 172        struct snd_ctl_elem_value *ucontrol)
 173{
 174        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 175
 176        if (spitz_jack_func == ucontrol->value.integer.value[0])
 177                return 0;
 178
 179        spitz_jack_func = ucontrol->value.integer.value[0];
 180        spitz_ext_control(&card->dapm);
 181        return 1;
 182}
 183
 184static int spitz_get_spk(struct snd_kcontrol *kcontrol,
 185        struct snd_ctl_elem_value *ucontrol)
 186{
 187        ucontrol->value.integer.value[0] = spitz_spk_func;
 188        return 0;
 189}
 190
 191static int spitz_set_spk(struct snd_kcontrol *kcontrol,
 192        struct snd_ctl_elem_value *ucontrol)
 193{
 194        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 195
 196        if (spitz_spk_func == ucontrol->value.integer.value[0])
 197                return 0;
 198
 199        spitz_spk_func = ucontrol->value.integer.value[0];
 200        spitz_ext_control(&card->dapm);
 201        return 1;
 202}
 203
 204static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
 205        struct snd_kcontrol *k, int event)
 206{
 207        gpio_set_value_cansleep(spitz_mic_gpio, SND_SOC_DAPM_EVENT_ON(event));
 208        return 0;
 209}
 210
 211/* spitz machine dapm widgets */
 212static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
 213        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 214        SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
 215        SND_SOC_DAPM_SPK("Ext Spk", NULL),
 216        SND_SOC_DAPM_LINE("Line Jack", NULL),
 217
 218        /* headset is a mic and mono headphone */
 219        SND_SOC_DAPM_HP("Headset Jack", NULL),
 220};
 221
 222/* Spitz machine audio_map */
 223static const struct snd_soc_dapm_route spitz_audio_map[] = {
 224
 225        /* headphone connected to LOUT1, ROUT1 */
 226        {"Headphone Jack", NULL, "LOUT1"},
 227        {"Headphone Jack", NULL, "ROUT1"},
 228
 229        /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
 230        {"Headset Jack", NULL, "ROUT1"},
 231
 232        /* ext speaker connected to LOUT2, ROUT2  */
 233        {"Ext Spk", NULL , "ROUT2"},
 234        {"Ext Spk", NULL , "LOUT2"},
 235
 236        /* mic is connected to input 1 - with bias */
 237        {"LINPUT1", NULL, "Mic Bias"},
 238        {"Mic Bias", NULL, "Mic Jack"},
 239
 240        /* line is connected to input 1 - no bias */
 241        {"LINPUT1", NULL, "Line Jack"},
 242};
 243
 244static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
 245        "Off"};
 246static const char *spk_function[] = {"On", "Off"};
 247static const struct soc_enum spitz_enum[] = {
 248        SOC_ENUM_SINGLE_EXT(5, jack_function),
 249        SOC_ENUM_SINGLE_EXT(2, spk_function),
 250};
 251
 252static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
 253        SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
 254                spitz_set_jack),
 255        SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
 256                spitz_set_spk),
 257};
 258
 259/* spitz digital audio interface glue - connects codec <--> CPU */
 260static struct snd_soc_dai_link spitz_dai = {
 261        .name = "wm8750",
 262        .stream_name = "WM8750",
 263        .cpu_dai_name = "pxa2xx-i2s",
 264        .codec_dai_name = "wm8750-hifi",
 265        .platform_name = "pxa-pcm-audio",
 266        .codec_name = "wm8750.0-001b",
 267        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 268                   SND_SOC_DAIFMT_CBS_CFS,
 269        .ops = &spitz_ops,
 270};
 271
 272/* spitz audio machine driver */
 273static struct snd_soc_card snd_soc_spitz = {
 274        .name = "Spitz",
 275        .owner = THIS_MODULE,
 276        .dai_link = &spitz_dai,
 277        .num_links = 1,
 278
 279        .controls = wm8750_spitz_controls,
 280        .num_controls = ARRAY_SIZE(wm8750_spitz_controls),
 281        .dapm_widgets = wm8750_dapm_widgets,
 282        .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
 283        .dapm_routes = spitz_audio_map,
 284        .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
 285        .fully_routed = true,
 286};
 287
 288static int spitz_probe(struct platform_device *pdev)
 289{
 290        struct snd_soc_card *card = &snd_soc_spitz;
 291        int ret;
 292
 293        if (machine_is_akita())
 294                spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
 295        else
 296                spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
 297
 298        ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
 299        if (ret)
 300                goto err1;
 301
 302        ret = gpio_direction_output(spitz_mic_gpio, 0);
 303        if (ret)
 304                goto err2;
 305
 306        card->dev = &pdev->dev;
 307
 308        ret = snd_soc_register_card(card);
 309        if (ret) {
 310                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
 311                        ret);
 312                goto err2;
 313        }
 314
 315        return 0;
 316
 317err2:
 318        gpio_free(spitz_mic_gpio);
 319err1:
 320        return ret;
 321}
 322
 323static int spitz_remove(struct platform_device *pdev)
 324{
 325        struct snd_soc_card *card = platform_get_drvdata(pdev);
 326
 327        snd_soc_unregister_card(card);
 328        gpio_free(spitz_mic_gpio);
 329        return 0;
 330}
 331
 332static struct platform_driver spitz_driver = {
 333        .driver         = {
 334                .name   = "spitz-audio",
 335                .pm     = &snd_soc_pm_ops,
 336        },
 337        .probe          = spitz_probe,
 338        .remove         = spitz_remove,
 339};
 340
 341module_platform_driver(spitz_driver);
 342
 343MODULE_AUTHOR("Richard Purdie");
 344MODULE_DESCRIPTION("ALSA SoC Spitz");
 345MODULE_LICENSE("GPL");
 346MODULE_ALIAS("platform:spitz-audio");
 347