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/consumer.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 "../codecs/wm8750.h"
  24#include "pxa2xx-i2s.h"
  25
  26#define SPITZ_HP        0
  27#define SPITZ_MIC       1
  28#define SPITZ_LINE      2
  29#define SPITZ_HEADSET   3
  30#define SPITZ_HP_OFF    4
  31#define SPITZ_SPK_ON    0
  32#define SPITZ_SPK_OFF   1
  33
  34 /* audio clock in Hz - rounded from 12.235MHz */
  35#define SPITZ_AUDIO_CLOCK 12288000
  36
  37static int spitz_jack_func;
  38static int spitz_spk_func;
  39static struct gpio_desc *gpiod_mic, *gpiod_mute_l, *gpiod_mute_r;
  40
  41static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
  42{
  43        snd_soc_dapm_mutex_lock(dapm);
  44
  45        if (spitz_spk_func == SPITZ_SPK_ON)
  46                snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
  47        else
  48                snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
  49
  50        /* set up jack connection */
  51        switch (spitz_jack_func) {
  52        case SPITZ_HP:
  53                /* enable and unmute hp jack, disable mic bias */
  54                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  55                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  56                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  57                snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
  58                gpiod_set_value(gpiod_mute_l, 1);
  59                gpiod_set_value(gpiod_mute_r, 1);
  60                break;
  61        case SPITZ_MIC:
  62                /* enable mic jack and bias, mute hp */
  63                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  64                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  65                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  66                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  67                gpiod_set_value(gpiod_mute_l, 0);
  68                gpiod_set_value(gpiod_mute_r, 0);
  69                break;
  70        case SPITZ_LINE:
  71                /* enable line jack, disable mic bias and mute hp */
  72                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  73                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  74                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  75                snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
  76                gpiod_set_value(gpiod_mute_l, 0);
  77                gpiod_set_value(gpiod_mute_r, 0);
  78                break;
  79        case SPITZ_HEADSET:
  80                /* enable and unmute headset jack enable mic bias, mute L hp */
  81                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  82                snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
  83                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  84                snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
  85                gpiod_set_value(gpiod_mute_l, 0);
  86                gpiod_set_value(gpiod_mute_r, 1);
  87                break;
  88        case SPITZ_HP_OFF:
  89
  90                /* jack removed, everything off */
  91                snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
  92                snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
  93                snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
  94                snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
  95                gpiod_set_value(gpiod_mute_l, 0);
  96                gpiod_set_value(gpiod_mute_r, 0);
  97                break;
  98        }
  99
 100        snd_soc_dapm_sync_unlocked(dapm);
 101
 102        snd_soc_dapm_mutex_unlock(dapm);
 103}
 104
 105static int spitz_startup(struct snd_pcm_substream *substream)
 106{
 107        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 108
 109        /* check the jack status at stream startup */
 110        spitz_ext_control(&rtd->card->dapm);
 111
 112        return 0;
 113}
 114
 115static int spitz_hw_params(struct snd_pcm_substream *substream,
 116        struct snd_pcm_hw_params *params)
 117{
 118        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 119        struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 120        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 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, WM8750_SYSCLK, 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 spitz_ops = {
 154        .startup = spitz_startup,
 155        .hw_params = spitz_hw_params,
 156};
 157
 158static int spitz_get_jack(struct snd_kcontrol *kcontrol,
 159        struct snd_ctl_elem_value *ucontrol)
 160{
 161        ucontrol->value.enumerated.item[0] = spitz_jack_func;
 162        return 0;
 163}
 164
 165static int spitz_set_jack(struct snd_kcontrol *kcontrol,
 166        struct snd_ctl_elem_value *ucontrol)
 167{
 168        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 169
 170        if (spitz_jack_func == ucontrol->value.enumerated.item[0])
 171                return 0;
 172
 173        spitz_jack_func = ucontrol->value.enumerated.item[0];
 174        spitz_ext_control(&card->dapm);
 175        return 1;
 176}
 177
 178static int spitz_get_spk(struct snd_kcontrol *kcontrol,
 179        struct snd_ctl_elem_value *ucontrol)
 180{
 181        ucontrol->value.enumerated.item[0] = spitz_spk_func;
 182        return 0;
 183}
 184
 185static int spitz_set_spk(struct snd_kcontrol *kcontrol,
 186        struct snd_ctl_elem_value *ucontrol)
 187{
 188        struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 189
 190        if (spitz_spk_func == ucontrol->value.enumerated.item[0])
 191                return 0;
 192
 193        spitz_spk_func = ucontrol->value.enumerated.item[0];
 194        spitz_ext_control(&card->dapm);
 195        return 1;
 196}
 197
 198static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
 199        struct snd_kcontrol *k, int event)
 200{
 201        gpiod_set_value_cansleep(gpiod_mic, SND_SOC_DAPM_EVENT_ON(event));
 202        return 0;
 203}
 204
 205/* spitz machine dapm widgets */
 206static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
 207        SND_SOC_DAPM_HP("Headphone Jack", NULL),
 208        SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
 209        SND_SOC_DAPM_SPK("Ext Spk", NULL),
 210        SND_SOC_DAPM_LINE("Line Jack", NULL),
 211
 212        /* headset is a mic and mono headphone */
 213        SND_SOC_DAPM_HP("Headset Jack", NULL),
 214};
 215
 216/* Spitz machine audio_map */
 217static const struct snd_soc_dapm_route spitz_audio_map[] = {
 218
 219        /* headphone connected to LOUT1, ROUT1 */
 220        {"Headphone Jack", NULL, "LOUT1"},
 221        {"Headphone Jack", NULL, "ROUT1"},
 222
 223        /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
 224        {"Headset Jack", NULL, "ROUT1"},
 225
 226        /* ext speaker connected to LOUT2, ROUT2  */
 227        {"Ext Spk", NULL, "ROUT2"},
 228        {"Ext Spk", NULL, "LOUT2"},
 229
 230        /* mic is connected to input 1 - with bias */
 231        {"LINPUT1", NULL, "Mic Bias"},
 232        {"Mic Bias", NULL, "Mic Jack"},
 233
 234        /* line is connected to input 1 - no bias */
 235        {"LINPUT1", NULL, "Line Jack"},
 236};
 237
 238static const char * const jack_function[] = {"Headphone", "Mic", "Line",
 239        "Headset", "Off"};
 240static const char * const spk_function[] = {"On", "Off"};
 241static const struct soc_enum spitz_enum[] = {
 242        SOC_ENUM_SINGLE_EXT(5, jack_function),
 243        SOC_ENUM_SINGLE_EXT(2, spk_function),
 244};
 245
 246static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
 247        SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
 248                spitz_set_jack),
 249        SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
 250                spitz_set_spk),
 251};
 252
 253/* spitz digital audio interface glue - connects codec <--> CPU */
 254SND_SOC_DAILINK_DEFS(wm8750,
 255        DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
 256        DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
 257        DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
 258
 259static struct snd_soc_dai_link spitz_dai = {
 260        .name = "wm8750",
 261        .stream_name = "WM8750",
 262        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 263                   SND_SOC_DAIFMT_CBC_CFC,
 264        .ops = &spitz_ops,
 265        SND_SOC_DAILINK_REG(wm8750),
 266};
 267
 268/* spitz audio machine driver */
 269static struct snd_soc_card snd_soc_spitz = {
 270        .name = "Spitz",
 271        .owner = THIS_MODULE,
 272        .dai_link = &spitz_dai,
 273        .num_links = 1,
 274
 275        .controls = wm8750_spitz_controls,
 276        .num_controls = ARRAY_SIZE(wm8750_spitz_controls),
 277        .dapm_widgets = wm8750_dapm_widgets,
 278        .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
 279        .dapm_routes = spitz_audio_map,
 280        .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
 281        .fully_routed = true,
 282};
 283
 284static int spitz_probe(struct platform_device *pdev)
 285{
 286        struct snd_soc_card *card = &snd_soc_spitz;
 287        int ret;
 288
 289        gpiod_mic = devm_gpiod_get(&pdev->dev, "mic", GPIOD_OUT_LOW);
 290        if (IS_ERR(gpiod_mic))
 291                return PTR_ERR(gpiod_mic);
 292        gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_LOW);
 293        if (IS_ERR(gpiod_mute_l))
 294                return PTR_ERR(gpiod_mute_l);
 295        gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_LOW);
 296        if (IS_ERR(gpiod_mute_r))
 297                return PTR_ERR(gpiod_mute_r);
 298
 299        card->dev = &pdev->dev;
 300
 301        ret = devm_snd_soc_register_card(&pdev->dev, card);
 302        if (ret)
 303                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
 304                        ret);
 305
 306        return ret;
 307}
 308
 309static struct platform_driver spitz_driver = {
 310        .driver         = {
 311                .name   = "spitz-audio",
 312                .pm     = &snd_soc_pm_ops,
 313        },
 314        .probe          = spitz_probe,
 315};
 316
 317module_platform_driver(spitz_driver);
 318
 319MODULE_AUTHOR("Richard Purdie");
 320MODULE_DESCRIPTION("ALSA SoC Spitz");
 321MODULE_LICENSE("GPL");
 322MODULE_ALIAS("platform:spitz-audio");
 323