aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-02-16 12:51:54 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-02-18 06:25:23 -0500
commit5de7f9b20069257aa5f0bb74723c8603adc5841a (patch)
tree076476094e6c9229c54c3d92a7a8625f48b42756 /sound
parent40135ea07190316a789b2edfbf7c8131598bdf81 (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.c68
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
58static struct clk *mclk;
56 59
57static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) 60static 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
197static 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
194static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { 223static 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
253static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = { 283static 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
305err_ssc: 361err_ssc:
362err_mclk:
363 clk_put(mclk);
364 mclk = NULL;
365err:
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
325module_init(at91sam9g20ek_init); 387module_init(at91sam9g20ek_init);