zylonite.c (6400B)
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(asoc_rtd_to_codec(rtd, 0), 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 = asoc_substream_to_rtd(substream); 79 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 80 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 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 125SND_SOC_DAILINK_DEFS(ac97, 126 DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97")), 127 DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-hifi")), 128 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 129 130SND_SOC_DAILINK_DEFS(ac97_aux, 131 DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-ac97-aux")), 132 DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-aux")), 133 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 134 135SND_SOC_DAILINK_DEFS(voice, 136 DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.2")), 137 DAILINK_COMP_ARRAY(COMP_CODEC("wm9713-codec", "wm9713-voice")), 138 DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio"))); 139 140static struct snd_soc_dai_link zylonite_dai[] = { 141{ 142 .name = "AC97", 143 .stream_name = "AC97 HiFi", 144 .init = zylonite_wm9713_init, 145 SND_SOC_DAILINK_REG(ac97), 146}, 147{ 148 .name = "AC97 Aux", 149 .stream_name = "AC97 Aux", 150 SND_SOC_DAILINK_REG(ac97_aux), 151}, 152{ 153 .name = "WM9713 Voice", 154 .stream_name = "WM9713 Voice", 155 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 156 SND_SOC_DAIFMT_CBS_CFS, 157 .ops = &zylonite_voice_ops, 158 SND_SOC_DAILINK_REG(voice), 159}, 160}; 161 162static int zylonite_probe(struct snd_soc_card *card) 163{ 164 int ret; 165 166 if (clk_pout) { 167 pout = clk_get(NULL, "CLK_POUT"); 168 if (IS_ERR(pout)) { 169 dev_err(card->dev, "Unable to obtain CLK_POUT: %ld\n", 170 PTR_ERR(pout)); 171 return PTR_ERR(pout); 172 } 173 174 ret = clk_enable(pout); 175 if (ret != 0) { 176 dev_err(card->dev, "Unable to enable CLK_POUT: %d\n", 177 ret); 178 clk_put(pout); 179 return ret; 180 } 181 182 dev_dbg(card->dev, "MCLK enabled at %luHz\n", 183 clk_get_rate(pout)); 184 } 185 186 return 0; 187} 188 189static int zylonite_remove(struct snd_soc_card *card) 190{ 191 if (clk_pout) { 192 clk_disable(pout); 193 clk_put(pout); 194 } 195 196 return 0; 197} 198 199static int zylonite_suspend_post(struct snd_soc_card *card) 200{ 201 if (clk_pout) 202 clk_disable(pout); 203 204 return 0; 205} 206 207static int zylonite_resume_pre(struct snd_soc_card *card) 208{ 209 int ret = 0; 210 211 if (clk_pout) { 212 ret = clk_enable(pout); 213 if (ret != 0) 214 dev_err(card->dev, "Unable to enable CLK_POUT: %d\n", 215 ret); 216 } 217 218 return ret; 219} 220 221static struct snd_soc_card zylonite = { 222 .name = "Zylonite", 223 .owner = THIS_MODULE, 224 .probe = &zylonite_probe, 225 .remove = &zylonite_remove, 226 .suspend_post = &zylonite_suspend_post, 227 .resume_pre = &zylonite_resume_pre, 228 .dai_link = zylonite_dai, 229 .num_links = ARRAY_SIZE(zylonite_dai), 230 231 .dapm_widgets = zylonite_dapm_widgets, 232 .num_dapm_widgets = ARRAY_SIZE(zylonite_dapm_widgets), 233 .dapm_routes = audio_map, 234 .num_dapm_routes = ARRAY_SIZE(audio_map), 235}; 236 237static struct platform_device *zylonite_snd_ac97_device; 238 239static int __init zylonite_init(void) 240{ 241 int ret; 242 243 zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1); 244 if (!zylonite_snd_ac97_device) 245 return -ENOMEM; 246 247 platform_set_drvdata(zylonite_snd_ac97_device, &zylonite); 248 249 ret = platform_device_add(zylonite_snd_ac97_device); 250 if (ret != 0) 251 platform_device_put(zylonite_snd_ac97_device); 252 253 return ret; 254} 255 256static void __exit zylonite_exit(void) 257{ 258 platform_device_unregister(zylonite_snd_ac97_device); 259} 260 261module_init(zylonite_init); 262module_exit(zylonite_exit); 263 264MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 265MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite"); 266MODULE_LICENSE("GPL");