linux/sound/soc/pxa/corgi.c
<<
>>
Prefs
   1/*
   2 * corgi.c  --  SoC audio for Corgi
   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#include <linux/module.h>
  17#include <linux/moduleparam.h>
  18#include <linux/timer.h>
  19#include <linux/i2c.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#include <sound/soc-dapm.h>
  27
  28#include <asm/mach-types.h>
  29#include <mach/corgi.h>
  30#include <mach/audio.h>
  31
  32#include "../codecs/wm8731.h"
  33#include "pxa2xx-pcm.h"
  34#include "pxa2xx-i2s.h"
  35
  36#define CORGI_HP        0
  37#define CORGI_MIC       1
  38#define CORGI_LINE      2
  39#define CORGI_HEADSET   3
  40#define CORGI_HP_OFF    4
  41#define CORGI_SPK_ON    0
  42#define CORGI_SPK_OFF   1
  43
  44 /* audio clock in Hz - rounded from 12.235MHz */
  45#define CORGI_AUDIO_CLOCK 12288000
  46
  47static int corgi_jack_func;
  48static int corgi_spk_func;
  49
  50static void corgi_ext_control(struct snd_soc_codec *codec)
  51{
  52        /* set up jack connection */
  53        switch (corgi_jack_func) {
  54        case CORGI_HP:
  55                /* set = unmute headphone */
  56                gpio_set_value(CORGI_GPIO_MUTE_L, 1);
  57                gpio_set_value(CORGI_GPIO_MUTE_R, 1);
  58                snd_soc_dapm_disable_pin(codec, "Mic Jack");
  59                snd_soc_dapm_disable_pin(codec, "Line Jack");
  60                snd_soc_dapm_enable_pin(codec, "Headphone Jack");
  61                snd_soc_dapm_disable_pin(codec, "Headset Jack");
  62                break;
  63        case CORGI_MIC:
  64                /* reset = mute headphone */
  65                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  66                gpio_set_value(CORGI_GPIO_MUTE_R, 0);
  67                snd_soc_dapm_enable_pin(codec, "Mic Jack");
  68                snd_soc_dapm_disable_pin(codec, "Line Jack");
  69                snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  70                snd_soc_dapm_disable_pin(codec, "Headset Jack");
  71                break;
  72        case CORGI_LINE:
  73                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  74                gpio_set_value(CORGI_GPIO_MUTE_R, 0);
  75                snd_soc_dapm_disable_pin(codec, "Mic Jack");
  76                snd_soc_dapm_enable_pin(codec, "Line Jack");
  77                snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  78                snd_soc_dapm_disable_pin(codec, "Headset Jack");
  79                break;
  80        case CORGI_HEADSET:
  81                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  82                gpio_set_value(CORGI_GPIO_MUTE_R, 1);
  83                snd_soc_dapm_enable_pin(codec, "Mic Jack");
  84                snd_soc_dapm_disable_pin(codec, "Line Jack");
  85                snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  86                snd_soc_dapm_enable_pin(codec, "Headset Jack");
  87                break;
  88        }
  89
  90        if (corgi_spk_func == CORGI_SPK_ON)
  91                snd_soc_dapm_enable_pin(codec, "Ext Spk");
  92        else
  93                snd_soc_dapm_disable_pin(codec, "Ext Spk");
  94
  95        /* signal a DAPM event */
  96        snd_soc_dapm_sync(codec);
  97}
  98
  99static int corgi_startup(struct snd_pcm_substream *substream)
 100{
 101        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 102        struct snd_soc_codec *codec = rtd->socdev->card->codec;
 103
 104        /* check the jack status at stream startup */
 105        corgi_ext_control(codec);
 106        return 0;
 107}
 108
 109/* we need to unmute the HP at shutdown as the mute burns power on corgi */
 110static void corgi_shutdown(struct snd_pcm_substream *substream)
 111{
 112        /* set = unmute headphone */
 113        gpio_set_value(CORGI_GPIO_MUTE_L, 1);
 114        gpio_set_value(CORGI_GPIO_MUTE_R, 1);
 115}
 116
 117static int corgi_hw_params(struct snd_pcm_substream *substream,
 118        struct snd_pcm_hw_params *params)
 119{
 120        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 121        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 122        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 123        unsigned int clk = 0;
 124        int ret = 0;
 125
 126        switch (params_rate(params)) {
 127        case 8000:
 128        case 16000:
 129        case 48000:
 130        case 96000:
 131                clk = 12288000;
 132                break;
 133        case 11025:
 134        case 22050:
 135        case 44100:
 136                clk = 11289600;
 137                break;
 138        }
 139
 140        /* set codec DAI configuration */
 141        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 142                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 143        if (ret < 0)
 144                return ret;
 145
 146        /* set cpu DAI configuration */
 147        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 148                SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
 149        if (ret < 0)
 150                return ret;
 151
 152        /* set the codec system clock for DAC and ADC */
 153        ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, clk,
 154                SND_SOC_CLOCK_IN);
 155        if (ret < 0)
 156                return ret;
 157
 158        /* set the I2S system clock as input (unused) */
 159        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 160                SND_SOC_CLOCK_IN);
 161        if (ret < 0)
 162                return ret;
 163
 164        return 0;
 165}
 166
 167static struct snd_soc_ops corgi_ops = {
 168        .startup = corgi_startup,
 169        .hw_params = corgi_hw_params,
 170        .shutdown = corgi_shutdown,
 171};
 172
 173static int corgi_get_jack(struct snd_kcontrol *kcontrol,
 174        struct snd_ctl_elem_value *ucontrol)
 175{
 176        ucontrol->value.integer.value[0] = corgi_jack_func;
 177        return 0;
 178}
 179
 180static int corgi_set_jack(struct snd_kcontrol *kcontrol,
 181        struct snd_ctl_elem_value *ucontrol)
 182{
 183        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 184
 185        if (corgi_jack_func == ucontrol->value.integer.value[0])
 186                return 0;
 187
 188        corgi_jack_func = ucontrol->value.integer.value[0];
 189        corgi_ext_control(codec);
 190        return 1;
 191}
 192
 193static int corgi_get_spk(struct snd_kcontrol *kcontrol,
 194        struct snd_ctl_elem_value *ucontrol)
 195{
 196        ucontrol->value.integer.value[0] = corgi_spk_func;
 197        return 0;
 198}
 199
 200static int corgi_set_spk(struct snd_kcontrol *kcontrol,
 201        struct snd_ctl_elem_value *ucontrol)
 202{
 203        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 204
 205        if (corgi_spk_func == ucontrol->value.integer.value[0])
 206                return 0;
 207
 208        corgi_spk_func = ucontrol->value.integer.value[0];
 209        corgi_ext_control(codec);
 210        return 1;
 211}
 212
 213static int corgi_amp_event(struct snd_soc_dapm_widget *w,
 214        struct snd_kcontrol *k, int event)
 215{
 216        gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
 217        return 0;
 218}
 219
 220static int corgi_mic_event(struct snd_soc_dapm_widget *w,
 221        struct snd_kcontrol *k, int event)
 222{
 223        gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
 224        return 0;
 225}
 226
 227/* corgi machine dapm widgets */
 228static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
 229SND_SOC_DAPM_HP("Headphone Jack", NULL),
 230SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
 231SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
 232SND_SOC_DAPM_LINE("Line Jack", NULL),
 233SND_SOC_DAPM_HP("Headset Jack", NULL),
 234};
 235
 236/* Corgi machine audio map (connections to the codec pins) */
 237static const struct snd_soc_dapm_route audio_map[] = {
 238
 239        /* headset Jack  - in = micin, out = LHPOUT*/
 240        {"Headset Jack", NULL, "LHPOUT"},
 241
 242        /* headphone connected to LHPOUT1, RHPOUT1 */
 243        {"Headphone Jack", NULL, "LHPOUT"},
 244        {"Headphone Jack", NULL, "RHPOUT"},
 245
 246        /* speaker connected to LOUT, ROUT */
 247        {"Ext Spk", NULL, "ROUT"},
 248        {"Ext Spk", NULL, "LOUT"},
 249
 250        /* mic is connected to MICIN (via right channel of headphone jack) */
 251        {"MICIN", NULL, "Mic Jack"},
 252
 253        /* Same as the above but no mic bias for line signals */
 254        {"MICIN", NULL, "Line Jack"},
 255};
 256
 257static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
 258        "Off"};
 259static const char *spk_function[] = {"On", "Off"};
 260static const struct soc_enum corgi_enum[] = {
 261        SOC_ENUM_SINGLE_EXT(5, jack_function),
 262        SOC_ENUM_SINGLE_EXT(2, spk_function),
 263};
 264
 265static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
 266        SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
 267                corgi_set_jack),
 268        SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
 269                corgi_set_spk),
 270};
 271
 272/*
 273 * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
 274 */
 275static int corgi_wm8731_init(struct snd_soc_codec *codec)
 276{
 277        int err;
 278
 279        snd_soc_dapm_nc_pin(codec, "LLINEIN");
 280        snd_soc_dapm_nc_pin(codec, "RLINEIN");
 281
 282        /* Add corgi specific controls */
 283        err = snd_soc_add_controls(codec, wm8731_corgi_controls,
 284                                ARRAY_SIZE(wm8731_corgi_controls));
 285        if (err < 0)
 286                return err;
 287
 288        /* Add corgi specific widgets */
 289        snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
 290                                  ARRAY_SIZE(wm8731_dapm_widgets));
 291
 292        /* Set up corgi specific audio path audio_map */
 293        snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 294
 295        snd_soc_dapm_sync(codec);
 296        return 0;
 297}
 298
 299/* corgi digital audio interface glue - connects codec <--> CPU */
 300static struct snd_soc_dai_link corgi_dai = {
 301        .name = "WM8731",
 302        .stream_name = "WM8731",
 303        .cpu_dai = &pxa_i2s_dai,
 304        .codec_dai = &wm8731_dai,
 305        .init = corgi_wm8731_init,
 306        .ops = &corgi_ops,
 307};
 308
 309/* corgi audio machine driver */
 310static struct snd_soc_card snd_soc_corgi = {
 311        .name = "Corgi",
 312        .platform = &pxa2xx_soc_platform,
 313        .dai_link = &corgi_dai,
 314        .num_links = 1,
 315};
 316
 317/* corgi audio subsystem */
 318static struct snd_soc_device corgi_snd_devdata = {
 319        .card = &snd_soc_corgi,
 320        .codec_dev = &soc_codec_dev_wm8731,
 321};
 322
 323static struct platform_device *corgi_snd_device;
 324
 325static int __init corgi_init(void)
 326{
 327        int ret;
 328
 329        if (!(machine_is_corgi() || machine_is_shepherd() ||
 330              machine_is_husky()))
 331                return -ENODEV;
 332
 333        corgi_snd_device = platform_device_alloc("soc-audio", -1);
 334        if (!corgi_snd_device)
 335                return -ENOMEM;
 336
 337        platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata);
 338        corgi_snd_devdata.dev = &corgi_snd_device->dev;
 339        ret = platform_device_add(corgi_snd_device);
 340
 341        if (ret)
 342                platform_device_put(corgi_snd_device);
 343
 344        return ret;
 345}
 346
 347static void __exit corgi_exit(void)
 348{
 349        platform_device_unregister(corgi_snd_device);
 350}
 351
 352module_init(corgi_init);
 353module_exit(corgi_exit);
 354
 355/* Module information */
 356MODULE_AUTHOR("Richard Purdie");
 357MODULE_DESCRIPTION("ALSA SoC Corgi");
 358MODULE_LICENSE("GPL");
 359