linux/sound/soc/samsung/smartq_wm8987.c
<<
>>
Prefs
   1/* sound/soc/samsung/smartq_wm8987.c
   2 *
   3 * Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
   4 *
   5 * Based on smdk6410_wm8987.c
   6 *     Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
   7 *     Graeme Gregory - graeme.gregory@wolfsonmicro.com
   8 *
   9 *  This program is free software; you can redistribute  it and/or modify it
  10 *  under  the terms of  the GNU General  Public License as published by the
  11 *  Free Software Foundation;  either version 2 of the  License, or (at your
  12 *  option) any later version.
  13 *
  14 */
  15
  16#include <linux/gpio/consumer.h>
  17#include <linux/module.h>
  18
  19#include <sound/soc.h>
  20#include <sound/jack.h>
  21
  22#include "i2s.h"
  23#include "../codecs/wm8750.h"
  24
  25/*
  26 * WM8987 is register compatible with WM8750, so using that as base driver.
  27 */
  28
  29static struct snd_soc_card snd_soc_smartq;
  30
  31static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
  32        struct snd_pcm_hw_params *params)
  33{
  34        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  35        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  36        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  37        unsigned int clk = 0;
  38        int ret;
  39
  40        switch (params_rate(params)) {
  41        case 8000:
  42        case 16000:
  43        case 32000:
  44        case 48000:
  45        case 96000:
  46                clk = 12288000;
  47                break;
  48        case 11025:
  49        case 22050:
  50        case 44100:
  51        case 88200:
  52                clk = 11289600;
  53                break;
  54        }
  55
  56        /* Use PCLK for I2S signal generation */
  57        ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
  58                                        0, SND_SOC_CLOCK_IN);
  59        if (ret < 0)
  60                return ret;
  61
  62        /* Gate the RCLK output on PAD */
  63        ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
  64                                        0, SND_SOC_CLOCK_IN);
  65        if (ret < 0)
  66                return ret;
  67
  68        /* set the codec system clock for DAC and ADC */
  69        ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
  70                                     SND_SOC_CLOCK_IN);
  71        if (ret < 0)
  72                return ret;
  73
  74        return 0;
  75}
  76
  77/*
  78 * SmartQ WM8987 HiFi DAI operations.
  79 */
  80static struct snd_soc_ops smartq_hifi_ops = {
  81        .hw_params = smartq_hifi_hw_params,
  82};
  83
  84static struct snd_soc_jack smartq_jack;
  85
  86static struct snd_soc_jack_pin smartq_jack_pins[] = {
  87        /* Disable speaker when headphone is plugged in */
  88        {
  89                .pin    = "Internal Speaker",
  90                .mask   = SND_JACK_HEADPHONE,
  91        },
  92};
  93
  94static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
  95        {
  96                .gpio           = -1,
  97                .name           = "headphone detect",
  98                .report         = SND_JACK_HEADPHONE,
  99                .debounce_time  = 200,
 100        },
 101};
 102
 103static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
 104        SOC_DAPM_PIN_SWITCH("Internal Speaker"),
 105        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
 106        SOC_DAPM_PIN_SWITCH("Internal Mic"),
 107};
 108
 109static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
 110                                struct snd_kcontrol *k,
 111                                int event)
 112{
 113        struct gpio_desc *gpio = snd_soc_card_get_drvdata(&snd_soc_smartq);
 114
 115        gpiod_set_value(gpio, SND_SOC_DAPM_EVENT_OFF(event));
 116
 117        return 0;
 118}
 119
 120static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
 121        SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
 122        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 123        SND_SOC_DAPM_MIC("Internal Mic", NULL),
 124};
 125
 126static const struct snd_soc_dapm_route audio_map[] = {
 127        {"Headphone Jack", NULL, "LOUT2"},
 128        {"Headphone Jack", NULL, "ROUT2"},
 129
 130        {"Internal Speaker", NULL, "LOUT2"},
 131        {"Internal Speaker", NULL, "ROUT2"},
 132
 133        {"Mic Bias", NULL, "Internal Mic"},
 134        {"LINPUT2", NULL, "Mic Bias"},
 135};
 136
 137static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
 138{
 139        struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
 140        int err = 0;
 141
 142        /* set endpoints to not connected */
 143        snd_soc_dapm_nc_pin(dapm, "LINPUT1");
 144        snd_soc_dapm_nc_pin(dapm, "RINPUT1");
 145        snd_soc_dapm_nc_pin(dapm, "OUT3");
 146        snd_soc_dapm_nc_pin(dapm, "ROUT1");
 147
 148        /* Headphone jack detection */
 149        err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
 150                                    SND_JACK_HEADPHONE, &smartq_jack,
 151                                    smartq_jack_pins,
 152                                    ARRAY_SIZE(smartq_jack_pins));
 153        if (err)
 154                return err;
 155
 156        err = snd_soc_jack_add_gpios(&smartq_jack,
 157                                     ARRAY_SIZE(smartq_jack_gpios),
 158                                     smartq_jack_gpios);
 159
 160        return err;
 161}
 162
 163static struct snd_soc_dai_link smartq_dai[] = {
 164        {
 165                .name           = "wm8987",
 166                .stream_name    = "SmartQ Hi-Fi",
 167                .cpu_dai_name   = "samsung-i2s.0",
 168                .codec_dai_name = "wm8750-hifi",
 169                .platform_name  = "samsung-i2s.0",
 170                .codec_name     = "wm8750.0-0x1a",
 171                .init           = smartq_wm8987_init,
 172                .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 173                                  SND_SOC_DAIFMT_CBS_CFS,
 174                .ops            = &smartq_hifi_ops,
 175        },
 176};
 177
 178static struct snd_soc_card snd_soc_smartq = {
 179        .name = "SmartQ",
 180        .owner = THIS_MODULE,
 181        .dai_link = smartq_dai,
 182        .num_links = ARRAY_SIZE(smartq_dai),
 183
 184        .dapm_widgets = wm8987_dapm_widgets,
 185        .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
 186        .dapm_routes = audio_map,
 187        .num_dapm_routes = ARRAY_SIZE(audio_map),
 188        .controls = wm8987_smartq_controls,
 189        .num_controls = ARRAY_SIZE(wm8987_smartq_controls),
 190};
 191
 192static int smartq_probe(struct platform_device *pdev)
 193{
 194        struct gpio_desc *gpio;
 195        int ret;
 196
 197        platform_set_drvdata(pdev, &snd_soc_smartq);
 198
 199        /* Initialise GPIOs used by amplifiers */
 200        gpio = devm_gpiod_get(&pdev->dev, "amplifiers shutdown",
 201                              GPIOD_OUT_HIGH);
 202        if (IS_ERR(gpio)) {
 203                dev_err(&pdev->dev, "Failed to register GPK12\n");
 204                ret = PTR_ERR(gpio);
 205                goto out;
 206        }
 207        snd_soc_card_set_drvdata(&snd_soc_smartq, gpio);
 208
 209        ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_smartq);
 210        if (ret)
 211                dev_err(&pdev->dev, "Failed to register card\n");
 212
 213out:
 214        return ret;
 215}
 216
 217static struct platform_driver smartq_driver = {
 218        .driver = {
 219                .name = "smartq-audio",
 220        },
 221        .probe = smartq_probe,
 222};
 223
 224module_platform_driver(smartq_driver);
 225
 226/* Module information */
 227MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
 228MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
 229MODULE_LICENSE("GPL");
 230