diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-02-16 12:51:54 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-02-18 06:25:23 -0500 |
commit | 5de7f9b20069257aa5f0bb74723c8603adc5841a (patch) | |
tree | 076476094e6c9229c54c3d92a7a8625f48b42756 /sound | |
parent | 40135ea07190316a789b2edfbf7c8131598bdf81 (diff) |
ASoC: Actively manage MCLK for AT91SAM9G20-EK
We have software control of the MCLK for the WM8731 so save a bit of
power by actively managing it within the machine driver, enabling it
only while the codec is active.
Once ASoC supports multiple boards and doesn't require the soc-audio
device the initial clock setup should be pushed down into the arch/arm
code but for now this reduces merge issues.
Tested-by: Sedji Gaouaou <sedji.gaouaou@atmel.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/atmel/sam9g20_wm8731.c | 68 |
1 files changed, 65 insertions, 3 deletions
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index b7efdc8119d4..ab32514a858d 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c | |||
@@ -53,6 +53,9 @@ | |||
53 | #include "atmel-pcm.h" | 53 | #include "atmel-pcm.h" |
54 | #include "atmel_ssc_dai.h" | 54 | #include "atmel_ssc_dai.h" |
55 | 55 | ||
56 | #define MCLK_RATE 12000000 | ||
57 | |||
58 | static struct clk *mclk; | ||
56 | 59 | ||
57 | static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) | 60 | static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) |
58 | { | 61 | { |
@@ -60,11 +63,12 @@ static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) | |||
60 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | 63 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; |
61 | int ret; | 64 | int ret; |
62 | 65 | ||
63 | /* codec system clock is supplied by PCK0, set to 12MHz */ | ||
64 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, | 66 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, |
65 | 12000000, SND_SOC_CLOCK_IN); | 67 | MCLK_RATE, SND_SOC_CLOCK_IN); |
66 | if (ret < 0) | 68 | if (ret < 0) { |
69 | clk_disable(mclk); | ||
67 | return ret; | 70 | return ret; |
71 | } | ||
68 | 72 | ||
69 | return 0; | 73 | return 0; |
70 | } | 74 | } |
@@ -190,6 +194,31 @@ static struct snd_soc_ops at91sam9g20ek_ops = { | |||
190 | .shutdown = at91sam9g20ek_shutdown, | 194 | .shutdown = at91sam9g20ek_shutdown, |
191 | }; | 195 | }; |
192 | 196 | ||
197 | static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card, | ||
198 | enum snd_soc_bias_level level) | ||
199 | { | ||
200 | static int mclk_on; | ||
201 | int ret = 0; | ||
202 | |||
203 | switch (level) { | ||
204 | case SND_SOC_BIAS_ON: | ||
205 | case SND_SOC_BIAS_PREPARE: | ||
206 | if (!mclk_on) | ||
207 | ret = clk_enable(mclk); | ||
208 | if (ret == 0) | ||
209 | mclk_on = 1; | ||
210 | break; | ||
211 | |||
212 | case SND_SOC_BIAS_OFF: | ||
213 | case SND_SOC_BIAS_STANDBY: | ||
214 | if (mclk_on) | ||
215 | clk_disable(mclk); | ||
216 | mclk_on = 0; | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | return ret; | ||
221 | } | ||
193 | 222 | ||
194 | static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { | 223 | static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { |
195 | SND_SOC_DAPM_MIC("Int Mic", NULL), | 224 | SND_SOC_DAPM_MIC("Int Mic", NULL), |
@@ -248,6 +277,7 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { | |||
248 | .platform = &atmel_soc_platform, | 277 | .platform = &atmel_soc_platform, |
249 | .dai_link = &at91sam9g20ek_dai, | 278 | .dai_link = &at91sam9g20ek_dai, |
250 | .num_links = 1, | 279 | .num_links = 1, |
280 | .set_bias_level = at91sam9g20ek_set_bias_level, | ||
251 | }; | 281 | }; |
252 | 282 | ||
253 | static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = { | 283 | static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = { |
@@ -267,12 +297,38 @@ static int __init at91sam9g20ek_init(void) | |||
267 | { | 297 | { |
268 | struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data; | 298 | struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data; |
269 | struct ssc_device *ssc = NULL; | 299 | struct ssc_device *ssc = NULL; |
300 | struct clk *pllb; | ||
270 | int ret; | 301 | int ret; |
271 | 302 | ||
272 | if (!machine_is_at91sam9g20ek()) | 303 | if (!machine_is_at91sam9g20ek()) |
273 | return -ENODEV; | 304 | return -ENODEV; |
274 | 305 | ||
275 | /* | 306 | /* |
307 | * Codec MCLK is supplied by PCK0 - set it up. | ||
308 | */ | ||
309 | mclk = clk_get(NULL, "pck0"); | ||
310 | if (IS_ERR(mclk)) { | ||
311 | printk(KERN_ERR "ASoC: Failed to get MCLK\n"); | ||
312 | ret = PTR_ERR(mclk); | ||
313 | goto err; | ||
314 | } | ||
315 | |||
316 | pllb = clk_get(NULL, "pllb"); | ||
317 | if (IS_ERR(mclk)) { | ||
318 | printk(KERN_ERR "ASoC: Failed to get PLLB\n"); | ||
319 | ret = PTR_ERR(mclk); | ||
320 | goto err_mclk; | ||
321 | } | ||
322 | ret = clk_set_parent(mclk, pllb); | ||
323 | clk_put(pllb); | ||
324 | if (ret != 0) { | ||
325 | printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); | ||
326 | goto err_mclk; | ||
327 | } | ||
328 | |||
329 | clk_set_rate(mclk, MCLK_RATE); | ||
330 | |||
331 | /* | ||
276 | * Request SSC device | 332 | * Request SSC device |
277 | */ | 333 | */ |
278 | ssc = ssc_request(0); | 334 | ssc = ssc_request(0); |
@@ -303,6 +359,10 @@ static int __init at91sam9g20ek_init(void) | |||
303 | return ret; | 359 | return ret; |
304 | 360 | ||
305 | err_ssc: | 361 | err_ssc: |
362 | err_mclk: | ||
363 | clk_put(mclk); | ||
364 | mclk = NULL; | ||
365 | err: | ||
306 | return ret; | 366 | return ret; |
307 | } | 367 | } |
308 | 368 | ||
@@ -320,6 +380,8 @@ static void __exit at91sam9g20ek_exit(void) | |||
320 | 380 | ||
321 | platform_device_unregister(at91sam9g20ek_snd_device); | 381 | platform_device_unregister(at91sam9g20ek_snd_device); |
322 | at91sam9g20ek_snd_device = NULL; | 382 | at91sam9g20ek_snd_device = NULL; |
383 | clk_put(mclk); | ||
384 | mclk = NULL; | ||
323 | } | 385 | } |
324 | 386 | ||
325 | module_init(at91sam9g20ek_init); | 387 | module_init(at91sam9g20ek_init); |