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.h>
  17#include <linux/module.h>
  18
  19#include <sound/soc.h>
  20#include <sound/jack.h>
  21
  22#include <asm/mach-types.h>
  23
  24#include "i2s.h"
  25#include "../codecs/wm8750.h"
  26
  27/*
  28 * WM8987 is register compatible with WM8750, so using that as base driver.
  29 */
  30
  31static struct snd_soc_card snd_soc_smartq;
  32
  33static int smartq_hifi_hw_params(struct snd_pcm_substream *substream,
  34        struct snd_pcm_hw_params *params)
  35{
  36        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  37        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  38        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  39        unsigned int clk = 0;
  40        int ret;
  41
  42        switch (params_rate(params)) {
  43        case 8000:
  44        case 16000:
  45        case 32000:
  46        case 48000:
  47        case 96000:
  48                clk = 12288000;
  49                break;
  50        case 11025:
  51        case 22050:
  52        case 44100:
  53        case 88200:
  54                clk = 11289600;
  55                break;
  56        }
  57
  58        /* set codec DAI configuration */
  59        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
  60                                             SND_SOC_DAIFMT_NB_NF |
  61                                             SND_SOC_DAIFMT_CBS_CFS);
  62        if (ret < 0)
  63                return ret;
  64
  65        /* set cpu DAI configuration */
  66        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
  67                                           SND_SOC_DAIFMT_NB_NF |
  68                                           SND_SOC_DAIFMT_CBS_CFS);
  69        if (ret < 0)
  70                return ret;
  71
  72        /* Use PCLK for I2S signal generation */
  73        ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
  74                                        0, SND_SOC_CLOCK_IN);
  75        if (ret < 0)
  76                return ret;
  77
  78        /* Gate the RCLK output on PAD */
  79        ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
  80                                        0, SND_SOC_CLOCK_IN);
  81        if (ret < 0)
  82                return ret;
  83
  84        /* set the codec system clock for DAC and ADC */
  85        ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
  86                                     SND_SOC_CLOCK_IN);
  87        if (ret < 0)
  88                return ret;
  89
  90        return 0;
  91}
  92
  93/*
  94 * SmartQ WM8987 HiFi DAI operations.
  95 */
  96static struct snd_soc_ops smartq_hifi_ops = {
  97        .hw_params = smartq_hifi_hw_params,
  98};
  99
 100static struct snd_soc_jack smartq_jack;
 101
 102static struct snd_soc_jack_pin smartq_jack_pins[] = {
 103        /* Disable speaker when headphone is plugged in */
 104        {
 105                .pin    = "Internal Speaker",
 106                .mask   = SND_JACK_HEADPHONE,
 107        },
 108};
 109
 110static struct snd_soc_jack_gpio smartq_jack_gpios[] = {
 111        {
 112                .gpio           = S3C64XX_GPL(12),
 113                .name           = "headphone detect",
 114                .report         = SND_JACK_HEADPHONE,
 115                .debounce_time  = 200,
 116        },
 117};
 118
 119static const struct snd_kcontrol_new wm8987_smartq_controls[] = {
 120        SOC_DAPM_PIN_SWITCH("Internal Speaker"),
 121        SOC_DAPM_PIN_SWITCH("Headphone Jack"),
 122        SOC_DAPM_PIN_SWITCH("Internal Mic"),
 123};
 124
 125static int smartq_speaker_event(struct snd_soc_dapm_widget *w,
 126                                struct snd_kcontrol *k,
 127                                int event)
 128{
 129        gpio_set_value(S3C64XX_GPK(12), SND_SOC_DAPM_EVENT_OFF(event));
 130
 131        return 0;
 132}
 133
 134static const struct snd_soc_dapm_widget wm8987_dapm_widgets[] = {
 135        SND_SOC_DAPM_SPK("Internal Speaker", smartq_speaker_event),
 136        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 137        SND_SOC_DAPM_MIC("Internal Mic", NULL),
 138};
 139
 140static const struct snd_soc_dapm_route audio_map[] = {
 141        {"Headphone Jack", NULL, "LOUT2"},
 142        {"Headphone Jack", NULL, "ROUT2"},
 143
 144        {"Internal Speaker", NULL, "LOUT2"},
 145        {"Internal Speaker", NULL, "ROUT2"},
 146
 147        {"Mic Bias", NULL, "Internal Mic"},
 148        {"LINPUT2", NULL, "Mic Bias"},
 149};
 150
 151static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd)
 152{
 153        struct snd_soc_codec *codec = rtd->codec;
 154        struct snd_soc_dapm_context *dapm = &codec->dapm;
 155        int err = 0;
 156
 157        /* set endpoints to not connected */
 158        snd_soc_dapm_nc_pin(dapm, "LINPUT1");
 159        snd_soc_dapm_nc_pin(dapm, "RINPUT1");
 160        snd_soc_dapm_nc_pin(dapm, "OUT3");
 161        snd_soc_dapm_nc_pin(dapm, "ROUT1");
 162
 163        /* set endpoints to default off mode */
 164        snd_soc_dapm_enable_pin(dapm, "Internal Speaker");
 165        snd_soc_dapm_enable_pin(dapm, "Internal Mic");
 166        snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
 167
 168        /* Headphone jack detection */
 169        err = snd_soc_jack_new(codec, "Headphone Jack",
 170                               SND_JACK_HEADPHONE, &smartq_jack);
 171        if (err)
 172                return err;
 173
 174        err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins),
 175                                    smartq_jack_pins);
 176        if (err)
 177                return err;
 178
 179        err = snd_soc_jack_add_gpios(&smartq_jack,
 180                                     ARRAY_SIZE(smartq_jack_gpios),
 181                                     smartq_jack_gpios);
 182
 183        return err;
 184}
 185
 186static struct snd_soc_dai_link smartq_dai[] = {
 187        {
 188                .name           = "wm8987",
 189                .stream_name    = "SmartQ Hi-Fi",
 190                .cpu_dai_name   = "samsung-i2s.0",
 191                .codec_dai_name = "wm8750-hifi",
 192                .platform_name  = "samsung-i2s.0",
 193                .codec_name     = "wm8750.0-0x1a",
 194                .init           = smartq_wm8987_init,
 195                .ops            = &smartq_hifi_ops,
 196        },
 197};
 198
 199static struct snd_soc_card snd_soc_smartq = {
 200        .name = "SmartQ",
 201        .owner = THIS_MODULE,
 202        .dai_link = smartq_dai,
 203        .num_links = ARRAY_SIZE(smartq_dai),
 204
 205        .dapm_widgets = wm8987_dapm_widgets,
 206        .num_dapm_widgets = ARRAY_SIZE(wm8987_dapm_widgets),
 207        .dapm_routes = audio_map,
 208        .num_dapm_routes = ARRAY_SIZE(audio_map),
 209        .controls = wm8987_smartq_controls,
 210        .num_controls = ARRAY_SIZE(wm8987_smartq_controls),
 211};
 212
 213static struct platform_device *smartq_snd_device;
 214
 215static int __init smartq_init(void)
 216{
 217        int ret;
 218
 219        if (!machine_is_smartq7() && !machine_is_smartq5()) {
 220                pr_info("Only SmartQ is supported by this ASoC driver\n");
 221                return -ENODEV;
 222        }
 223
 224        smartq_snd_device = platform_device_alloc("soc-audio", -1);
 225        if (!smartq_snd_device)
 226                return -ENOMEM;
 227
 228        platform_set_drvdata(smartq_snd_device, &snd_soc_smartq);
 229
 230        ret = platform_device_add(smartq_snd_device);
 231        if (ret) {
 232                platform_device_put(smartq_snd_device);
 233                return ret;
 234        }
 235
 236        /* Initialise GPIOs used by amplifiers */
 237        ret = gpio_request(S3C64XX_GPK(12), "amplifiers shutdown");
 238        if (ret) {
 239                dev_err(&smartq_snd_device->dev, "Failed to register GPK12\n");
 240                goto err_unregister_device;
 241        }
 242
 243        /* Disable amplifiers */
 244        ret = gpio_direction_output(S3C64XX_GPK(12), 1);
 245        if (ret) {
 246                dev_err(&smartq_snd_device->dev, "Failed to configure GPK12\n");
 247                goto err_free_gpio_amp_shut;
 248        }
 249
 250        return 0;
 251
 252err_free_gpio_amp_shut:
 253        gpio_free(S3C64XX_GPK(12));
 254err_unregister_device:
 255        platform_device_unregister(smartq_snd_device);
 256
 257        return ret;
 258}
 259
 260static void __exit smartq_exit(void)
 261{
 262        gpio_free(S3C64XX_GPK(12));
 263        snd_soc_jack_free_gpios(&smartq_jack, ARRAY_SIZE(smartq_jack_gpios),
 264                                smartq_jack_gpios);
 265
 266        platform_device_unregister(smartq_snd_device);
 267}
 268
 269module_init(smartq_init);
 270module_exit(smartq_exit);
 271
 272/* Module information */
 273MODULE_AUTHOR("Maurus Cuelenaere <mcuelenaere@gmail.com>");
 274MODULE_DESCRIPTION("ALSA SoC SmartQ WM8987");
 275MODULE_LICENSE("GPL");
 276