linux/sound/soc/pxa/corgi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * corgi.c  --  SoC audio for Corgi
   4 *
   5 * Copyright 2005 Wolfson Microelectronics PLC.
   6 * Copyright 2005 Openedhand Ltd.
   7 *
   8 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
   9 *          Richard Purdie <richard@openedhand.com>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/moduleparam.h>
  14#include <linux/timer.h>
  15#include <linux/i2c.h>
  16#include <linux/interrupt.h>
  17#include <linux/platform_device.h>
  18#include <linux/gpio.h>
  19#include <sound/core.h>
  20#include <sound/pcm.h>
  21#include <sound/soc.h>
  22
  23#include <asm/mach-types.h>
  24#include <mach/corgi.h>
  25#include <mach/audio.h>
  26
  27#include "../codecs/wm8731.h"
  28#include "pxa2xx-i2s.h"
  29
  30#define CORGI_HP        0
  31#define CORGI_MIC       1
  32#define CORGI_LINE      2
  33#define CORGI_HEADSET   3
  34#define CORGI_HP_OFF    4
  35#define CORGI_SPK_ON    0
  36#define CORGI_SPK_OFF   1
  37
  38 /* audio clock in Hz - rounded from 12.235MHz */
  39#define CORGI_AUDIO_CLOCK 12288000
  40
  41static int corgi_jack_func;
  42static int corgi_spk_func;
  43
  44static void corgi_ext_control(struct snd_soc_dapm_context *dapm)
  45{
  46        snd_soc_dapm_mutex_lock(dapm);
  47
  48        /* set up jack connection */
  49        switch (corgi_jack_func) {
  50        case CORGI_HP:
  51                /* set = unmute headphone */
  52                gpio_set_value(CORGI_GPIO_MUTE_L, 1);
  53                gpio_set_value(CORGI_GPIO_MUTE_R, 1);
  54                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  55                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  56                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  57                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  58                break;
  59        case CORGI_MIC:
  60                /* reset = mute headphone */
  61                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  62                gpio_set_value(CORGI_GPIO_MUTE_R, 0);
  63                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  64                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  65                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  66                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  67                break;
  68        case CORGI_LINE:
  69                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  70                gpio_set_value(CORGI_GPIO_MUTE_R, 0);
  71                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  72                snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
  73                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  74                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  75                break;
  76        case CORGI_HEADSET:
  77                gpio_set_value(CORGI_GPIO_MUTE_L, 0);
  78                gpio_set_value(CORGI_GPIO_MUTE_R, 1);
  79                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  80                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  81                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  82                snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
  83                break;
  84        }
  85
  86        if (corgi_spk_func == CORGI_SPK_ON)
  87                snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
  88        else
  89                snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
  90
  91        /* signal a DAPM event */
  92        snd_soc_dapm_sync_unlocked(dapm);
  93
  94        snd_soc_dapm_mutex_unlock(dapm);
  95}
  96
  97static int corgi_startup(struct snd_pcm_substream *substream)
  98{
  99        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 100
 101        /* check the jack status at stream startup */
 102        corgi_ext_control(&rtd->card->dapm);
 103
 104        return 0;
 105}
 106
 107/* we need to unmute the HP at shutdown as the mute burns power on corgi */
 108static void corgi_shutdown(struct snd_pcm_substream *substream)
 109{
 110        /* set = unmute headphone */
 111        gpio_set_value(CORGI_GPIO_MUTE_L, 1);
 112        gpio_set_value(CORGI_GPIO_MUTE_R, 1);
 113}
 114
 115static int corgi_hw_params(struct snd_pcm_substream *substream,
 116        struct snd_pcm_hw_params *params)
 117{
 118        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 119        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 120        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 121        unsigned int clk = 0;
 122        int ret = 0;
 123
 124        switch (params_rate(params)) {
 125        case 8000:
 126        case 16000:
 127        case 48000:
 128        case 96000:
 129                clk = 12288000;
 130                break;
 131        case 11025:
 132        case 22050:
 133        case 44100:
 134                clk = 11289600;
 135                break;
 136        }
 137
 138        /* set the codec system clock for DAC and ADC */
 139        ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, clk,
 140                SND_SOC_CLOCK_IN);
 141        if (ret < 0)
 142                return ret;
 143
 144        /* set the I2S system clock as input (unused) */
 145        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
 146                SND_SOC_CLOCK_IN);
 147        if (ret < 0)
 148                return ret;
 149
 150        return 0;
 151}
 152
 153static const struct snd_soc_ops corgi_ops = {
 154        .startup = corgi_startup,
 155        .hw_params = corgi_hw_params,
 156        .shutdown = corgi_shutdown,
 157};
 158
 159static int corgi_get_jack(struct snd_kcontrol *kcontrol,
 160        struct snd_ctl_elem_value *ucontrol)
 161{
 162        ucontrol->value.enumerated.item[0] = corgi_jack_func;
 163        return 0;
 164}
 165
 166static int corgi_set_jack(struct snd_kcontrol *kcontrol,
 167        struct snd_ctl_elem_value *ucontrol)
 168{
 169        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 170
 171        if (corgi_jack_func == ucontrol->value.enumerated.item[0])
 172                return 0;
 173
 174        corgi_jack_func = ucontrol->value.enumerated.item[0];
 175        corgi_ext_control(&card->dapm);
 176        return 1;
 177}
 178
 179static int corgi_get_spk(struct snd_kcontrol *kcontrol,
 180        struct snd_ctl_elem_value *ucontrol)
 181{
 182        ucontrol->value.enumerated.item[0] = corgi_spk_func;
 183        return 0;
 184}
 185
 186static int corgi_set_spk(struct snd_kcontrol *kcontrol,
 187        struct snd_ctl_elem_value *ucontrol)
 188{
 189        struct snd_soc_card *card =  snd_kcontrol_chip(kcontrol);
 190
 191        if (corgi_spk_func == ucontrol->value.enumerated.item[0])
 192                return 0;
 193
 194        corgi_spk_func = ucontrol->value.enumerated.item[0];
 195        corgi_ext_control(&card->dapm);
 196        return 1;
 197}
 198
 199static int corgi_amp_event(struct snd_soc_dapm_widget *w,
 200        struct snd_kcontrol *k, int event)
 201{
 202        gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
 203        return 0;
 204}
 205
 206static int corgi_mic_event(struct snd_soc_dapm_widget *w,
 207        struct snd_kcontrol *k, int event)
 208{
 209        gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
 210        return 0;
 211}
 212
 213/* corgi machine dapm widgets */
 214static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
 215SND_SOC_DAPM_HP("Headphone Jack", NULL),
 216SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
 217SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
 218SND_SOC_DAPM_LINE("Line Jack", NULL),
 219SND_SOC_DAPM_HP("Headset Jack", NULL),
 220};
 221
 222/* Corgi machine audio map (connections to the codec pins) */
 223static const struct snd_soc_dapm_route corgi_audio_map[] = {
 224
 225        /* headset Jack  - in = micin, out = LHPOUT*/
 226        {"Headset Jack", NULL, "LHPOUT"},
 227
 228        /* headphone connected to LHPOUT1, RHPOUT1 */
 229        {"Headphone Jack", NULL, "LHPOUT"},
 230        {"Headphone Jack", NULL, "RHPOUT"},
 231
 232        /* speaker connected to LOUT, ROUT */
 233        {"Ext Spk", NULL, "ROUT"},
 234        {"Ext Spk", NULL, "LOUT"},
 235
 236        /* mic is connected to MICIN (via right channel of headphone jack) */
 237        {"MICIN", NULL, "Mic Jack"},
 238
 239        /* Same as the above but no mic bias for line signals */
 240        {"MICIN", NULL, "Line Jack"},
 241};
 242
 243static const char * const jack_function[] = {"Headphone", "Mic", "Line",
 244        "Headset", "Off"};
 245static const char * const spk_function[] = {"On", "Off"};
 246static const struct soc_enum corgi_enum[] = {
 247        SOC_ENUM_SINGLE_EXT(5, jack_function),
 248        SOC_ENUM_SINGLE_EXT(2, spk_function),
 249};
 250
 251static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
 252        SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
 253                corgi_set_jack),
 254        SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
 255                corgi_set_spk),
 256};
 257
 258/* corgi digital audio interface glue - connects codec <--> CPU */
 259SND_SOC_DAILINK_DEFS(wm8731,
 260        DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
 261        DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
 262        DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
 263
 264static struct snd_soc_dai_link corgi_dai = {
 265        .name = "WM8731",
 266        .stream_name = "WM8731",
 267        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 268                   SND_SOC_DAIFMT_CBS_CFS,
 269        .ops = &corgi_ops,
 270        SND_SOC_DAILINK_REG(wm8731),
 271};
 272
 273/* corgi audio machine driver */
 274static struct snd_soc_card corgi = {
 275        .name = "Corgi",
 276        .owner = THIS_MODULE,
 277        .dai_link = &corgi_dai,
 278        .num_links = 1,
 279
 280        .controls = wm8731_corgi_controls,
 281        .num_controls = ARRAY_SIZE(wm8731_corgi_controls),
 282        .dapm_widgets = wm8731_dapm_widgets,
 283        .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
 284        .dapm_routes = corgi_audio_map,
 285        .num_dapm_routes = ARRAY_SIZE(corgi_audio_map),
 286        .fully_routed = true,
 287};
 288
 289static int corgi_probe(struct platform_device *pdev)
 290{
 291        struct snd_soc_card *card = &corgi;
 292        int ret;
 293
 294        card->dev = &pdev->dev;
 295
 296        ret = devm_snd_soc_register_card(&pdev->dev, card);
 297        if (ret)
 298                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
 299                        ret);
 300        return ret;
 301}
 302
 303static struct platform_driver corgi_driver = {
 304        .driver         = {
 305                .name   = "corgi-audio",
 306                .pm     = &snd_soc_pm_ops,
 307        },
 308        .probe          = corgi_probe,
 309};
 310
 311module_platform_driver(corgi_driver);
 312
 313/* Module information */
 314MODULE_AUTHOR("Richard Purdie");
 315MODULE_DESCRIPTION("ALSA SoC Corgi");
 316MODULE_LICENSE("GPL");
 317MODULE_ALIAS("platform:corgi-audio");
 318