linux/sound/soc/pxa/zylonite.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * zylonite.c  --  SoC audio for Zylonite
   4 *
   5 * Copyright 2008 Wolfson Microelectronics PLC.
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/moduleparam.h>
  11#include <linux/device.h>
  12#include <linux/clk.h>
  13#include <linux/i2c.h>
  14#include <sound/core.h>
  15#include <sound/pcm.h>
  16#include <sound/pcm_params.h>
  17#include <sound/soc.h>
  18
  19#include "../codecs/wm9713.h"
  20#include "pxa-ssp.h"
  21
  22/*
  23 * There is a physical switch SW15 on the board which changes the MCLK
  24 * for the WM9713 between the standard AC97 master clock and the
  25 * output of the CLK_POUT signal from the PXA.
  26 */
  27static int clk_pout;
  28module_param(clk_pout, int, 0);
  29MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board).");
  30
  31static struct clk *pout;
  32
  33static struct snd_soc_card zylonite;
  34
  35static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
  36        SND_SOC_DAPM_HP("Headphone", NULL),
  37        SND_SOC_DAPM_MIC("Headset Microphone", NULL),
  38        SND_SOC_DAPM_MIC("Handset Microphone", NULL),
  39        SND_SOC_DAPM_SPK("Multiactor", NULL),
  40        SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
  41};
  42
  43/* Currently supported audio map */
  44static const struct snd_soc_dapm_route audio_map[] = {
  45
  46        /* Headphone output connected to HPL/HPR */
  47        { "Headphone", NULL,  "HPL" },
  48        { "Headphone", NULL,  "HPR" },
  49
  50        /* On-board earpiece */
  51        { "Headset Earpiece", NULL, "OUT3" },
  52
  53        /* Headphone mic */
  54        { "MIC2A", NULL, "Mic Bias" },
  55        { "Mic Bias", NULL, "Headset Microphone" },
  56
  57        /* On-board mic */
  58        { "MIC1", NULL, "Mic Bias" },
  59        { "Mic Bias", NULL, "Handset Microphone" },
  60
  61        /* Multiactor differentially connected over SPKL/SPKR */
  62        { "Multiactor", NULL, "SPKL" },
  63        { "Multiactor", NULL, "SPKR" },
  64};
  65
  66static int zylonite_wm9713_init(struct snd_soc_pcm_runtime *rtd)
  67{
  68        if (clk_pout)
  69                snd_soc_dai_set_pll(rtd->codec_dai, 0, 0,
  70                                    clk_get_rate(pout), 0);
  71
  72        return 0;
  73}
  74
  75static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
  76                                    struct snd_pcm_hw_params *params)
  77{
  78        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  79        struct snd_soc_dai *codec_dai = rtd->codec_dai;
  80        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  81        unsigned int wm9713_div = 0;
  82        int ret = 0;
  83        int rate = params_rate(params);
  84
  85        /* Only support ratios that we can generate neatly from the AC97
  86         * based master clock - in particular, this excludes 44.1kHz.
  87         * In most applications the voice DAC will be used for telephony
  88         * data so multiples of 8kHz will be the common case.
  89         */
  90        switch (rate) {
  91        case 8000:
  92                wm9713_div = 12;
  93                break;
  94        case 16000:
  95                wm9713_div = 6;
  96                break;
  97        case 48000:
  98                wm9713_div = 2;
  99                break;
 100        default:
 101                /* Don't support OSS emulation */
 102                return -EINVAL;
 103        }
 104
 105        ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
 106        if (ret < 0)
 107                return ret;
 108
 109        if (clk_pout)
 110                ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV,
 111                                             WM9713_PCMDIV(wm9713_div));
 112        else
 113                ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
 114                                             WM9713_PCMDIV(wm9713_div));
 115        if (ret < 0)
 116                return ret;
 117
 118        return 0;
 119}
 120
 121static const struct snd_soc_ops zylonite_voice_ops = {
 122        .hw_params = zylonite_voice_hw_params,
 123};
 124
 125static struct snd_soc_dai_link zylonite_dai[] = {
 126{
 127        .name = "AC97",
 128        .stream_name = "AC97 HiFi",
 129        .codec_name = "wm9713-codec",
 130        .platform_name = "pxa-pcm-audio",
 131        .cpu_dai_name = "pxa2xx-ac97",
 132        .codec_dai_name = "wm9713-hifi",
 133        .init = zylonite_wm9713_init,
 134},
 135{
 136        .name = "AC97 Aux",
 137        .stream_name = "AC97 Aux",
 138        .codec_name = "wm9713-codec",
 139        .platform_name = "pxa-pcm-audio",
 140        .cpu_dai_name = "pxa2xx-ac97-aux",
 141        .codec_dai_name = "wm9713-aux",
 142},
 143{
 144        .name = "WM9713 Voice",
 145        .stream_name = "WM9713 Voice",
 146        .codec_name = "wm9713-codec",
 147        .platform_name = "pxa-pcm-audio",
 148        .cpu_dai_name = "pxa-ssp-dai.2",
 149        .codec_dai_name = "wm9713-voice",
 150        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 151                   SND_SOC_DAIFMT_CBS_CFS,
 152        .ops = &zylonite_voice_ops,
 153},
 154};
 155
 156static int zylonite_probe(struct snd_soc_card *card)
 157{
 158        int ret;
 159
 160        if (clk_pout) {
 161                pout = clk_get(NULL, "CLK_POUT");
 162                if (IS_ERR(pout)) {
 163                        dev_err(card->dev, "Unable to obtain CLK_POUT: %ld\n",
 164                                PTR_ERR(pout));
 165                        return PTR_ERR(pout);
 166                }
 167
 168                ret = clk_enable(pout);
 169                if (ret != 0) {
 170                        dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
 171                                ret);
 172                        clk_put(pout);
 173                        return ret;
 174                }
 175
 176                dev_dbg(card->dev, "MCLK enabled at %luHz\n",
 177                        clk_get_rate(pout));
 178        }
 179
 180        return 0;
 181}
 182
 183static int zylonite_remove(struct snd_soc_card *card)
 184{
 185        if (clk_pout) {
 186                clk_disable(pout);
 187                clk_put(pout);
 188        }
 189
 190        return 0;
 191}
 192
 193static int zylonite_suspend_post(struct snd_soc_card *card)
 194{
 195        if (clk_pout)
 196                clk_disable(pout);
 197
 198        return 0;
 199}
 200
 201static int zylonite_resume_pre(struct snd_soc_card *card)
 202{
 203        int ret = 0;
 204
 205        if (clk_pout) {
 206                ret = clk_enable(pout);
 207                if (ret != 0)
 208                        dev_err(card->dev, "Unable to enable CLK_POUT: %d\n",
 209                                ret);
 210        }
 211
 212        return ret;
 213}
 214
 215static struct snd_soc_card zylonite = {
 216        .name = "Zylonite",
 217        .owner = THIS_MODULE,
 218        .probe = &zylonite_probe,
 219        .remove = &zylonite_remove,
 220        .suspend_post = &zylonite_suspend_post,
 221        .resume_pre = &zylonite_resume_pre,
 222        .dai_link = zylonite_dai,
 223        .num_links = ARRAY_SIZE(zylonite_dai),
 224
 225        .dapm_widgets = zylonite_dapm_widgets,
 226        .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets),
 227        .dapm_routes = audio_map,
 228        .num_dapm_routes = ARRAY_SIZE(audio_map),
 229};
 230
 231static struct platform_device *zylonite_snd_ac97_device;
 232
 233static int __init zylonite_init(void)
 234{
 235        int ret;
 236
 237        zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
 238        if (!zylonite_snd_ac97_device)
 239                return -ENOMEM;
 240
 241        platform_set_drvdata(zylonite_snd_ac97_device, &zylonite);
 242
 243        ret = platform_device_add(zylonite_snd_ac97_device);
 244        if (ret != 0)
 245                platform_device_put(zylonite_snd_ac97_device);
 246
 247        return ret;
 248}
 249
 250static void __exit zylonite_exit(void)
 251{
 252        platform_device_unregister(zylonite_snd_ac97_device);
 253}
 254
 255module_init(zylonite_init);
 256module_exit(zylonite_exit);
 257
 258MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 259MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
 260MODULE_LICENSE("GPL");
 261