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