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