aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/atmel/sam9g20_wm8731.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/atmel/sam9g20_wm8731.c')
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c124
1 files changed, 111 insertions, 13 deletions
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 6ea04be911d0..173a239a541c 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -36,6 +36,7 @@
36#include <linux/timer.h> 36#include <linux/timer.h>
37#include <linux/interrupt.h> 37#include <linux/interrupt.h>
38#include <linux/platform_device.h> 38#include <linux/platform_device.h>
39#include <linux/i2c.h>
39 40
40#include <linux/atmel-ssc.h> 41#include <linux/atmel-ssc.h>
41 42
@@ -45,6 +46,7 @@
45#include <sound/soc.h> 46#include <sound/soc.h>
46#include <sound/soc-dapm.h> 47#include <sound/soc-dapm.h>
47 48
49#include <asm/mach-types.h>
48#include <mach/hardware.h> 50#include <mach/hardware.h>
49#include <mach/gpio.h> 51#include <mach/gpio.h>
50 52
@@ -52,6 +54,9 @@
52#include "atmel-pcm.h" 54#include "atmel-pcm.h"
53#include "atmel_ssc_dai.h" 55#include "atmel_ssc_dai.h"
54 56
57#define MCLK_RATE 12000000
58
59static struct clk *mclk;
55 60
56static int at91sam9g20ek_startup(struct snd_pcm_substream *substream) 61static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
57{ 62{
@@ -59,11 +64,12 @@ static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
59 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; 64 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
60 int ret; 65 int ret;
61 66
62 /* codec system clock is supplied by PCK0, set to 12MHz */
63 ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, 67 ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
64 12000000, SND_SOC_CLOCK_IN); 68 MCLK_RATE, SND_SOC_CLOCK_IN);
65 if (ret < 0) 69 if (ret < 0) {
70 clk_disable(mclk);
66 return ret; 71 return ret;
72 }
67 73
68 return 0; 74 return 0;
69} 75}
@@ -189,6 +195,31 @@ static struct snd_soc_ops at91sam9g20ek_ops = {
189 .shutdown = at91sam9g20ek_shutdown, 195 .shutdown = at91sam9g20ek_shutdown,
190}; 196};
191 197
198static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
199 enum snd_soc_bias_level level)
200{
201 static int mclk_on;
202 int ret = 0;
203
204 switch (level) {
205 case SND_SOC_BIAS_ON:
206 case SND_SOC_BIAS_PREPARE:
207 if (!mclk_on)
208 ret = clk_enable(mclk);
209 if (ret == 0)
210 mclk_on = 1;
211 break;
212
213 case SND_SOC_BIAS_OFF:
214 case SND_SOC_BIAS_STANDBY:
215 if (mclk_on)
216 clk_disable(mclk);
217 mclk_on = 0;
218 break;
219 }
220
221 return ret;
222}
192 223
193static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { 224static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
194 SND_SOC_DAPM_MIC("Int Mic", NULL), 225 SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -243,21 +274,48 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
243}; 274};
244 275
245static struct snd_soc_card snd_soc_at91sam9g20ek = { 276static struct snd_soc_card snd_soc_at91sam9g20ek = {
246 .name = "WM8731", 277 .name = "AT91SAMG20-EK",
247 .platform = &atmel_soc_platform, 278 .platform = &atmel_soc_platform,
248 .dai_link = &at91sam9g20ek_dai, 279 .dai_link = &at91sam9g20ek_dai,
249 .num_links = 1, 280 .num_links = 1,
281 .set_bias_level = at91sam9g20ek_set_bias_level,
250}; 282};
251 283
252static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = { 284/*
253 .i2c_bus = 0, 285 * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
254 .i2c_address = 0x1b, 286 * New drivers should register the wm8731 I2C device in the machine
255}; 287 * setup code (under arch/arm for ARM systems).
288 */
289static int wm8731_i2c_register(void)
290{
291 struct i2c_board_info info;
292 struct i2c_adapter *adapter;
293 struct i2c_client *client;
294
295 memset(&info, 0, sizeof(struct i2c_board_info));
296 info.addr = 0x1b;
297 strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
298
299 adapter = i2c_get_adapter(0);
300 if (!adapter) {
301 printk(KERN_ERR "can't get i2c adapter 0\n");
302 return -ENODEV;
303 }
304
305 client = i2c_new_device(adapter, &info);
306 i2c_put_adapter(adapter);
307 if (!client) {
308 printk(KERN_ERR "can't add i2c device at 0x%x\n",
309 (unsigned int)info.addr);
310 return -ENODEV;
311 }
312
313 return 0;
314}
256 315
257static struct snd_soc_device at91sam9g20ek_snd_devdata = { 316static struct snd_soc_device at91sam9g20ek_snd_devdata = {
258 .card = &snd_soc_at91sam9g20ek, 317 .card = &snd_soc_at91sam9g20ek,
259 .codec_dev = &soc_codec_dev_wm8731, 318 .codec_dev = &soc_codec_dev_wm8731,
260 .codec_data = &at91sam9g20ek_wm8731_setup,
261}; 319};
262 320
263static struct platform_device *at91sam9g20ek_snd_device; 321static struct platform_device *at91sam9g20ek_snd_device;
@@ -266,23 +324,56 @@ static int __init at91sam9g20ek_init(void)
266{ 324{
267 struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data; 325 struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
268 struct ssc_device *ssc = NULL; 326 struct ssc_device *ssc = NULL;
327 struct clk *pllb;
269 int ret; 328 int ret;
270 329
330 if (!machine_is_at91sam9g20ek())
331 return -ENODEV;
332
333 /*
334 * Codec MCLK is supplied by PCK0 - set it up.
335 */
336 mclk = clk_get(NULL, "pck0");
337 if (IS_ERR(mclk)) {
338 printk(KERN_ERR "ASoC: Failed to get MCLK\n");
339 ret = PTR_ERR(mclk);
340 goto err;
341 }
342
343 pllb = clk_get(NULL, "pllb");
344 if (IS_ERR(mclk)) {
345 printk(KERN_ERR "ASoC: Failed to get PLLB\n");
346 ret = PTR_ERR(mclk);
347 goto err_mclk;
348 }
349 ret = clk_set_parent(mclk, pllb);
350 clk_put(pllb);
351 if (ret != 0) {
352 printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
353 goto err_mclk;
354 }
355
356 clk_set_rate(mclk, MCLK_RATE);
357
271 /* 358 /*
272 * Request SSC device 359 * Request SSC device
273 */ 360 */
274 ssc = ssc_request(0); 361 ssc = ssc_request(0);
275 if (IS_ERR(ssc)) { 362 if (IS_ERR(ssc)) {
363 printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
276 ret = PTR_ERR(ssc); 364 ret = PTR_ERR(ssc);
277 ssc = NULL; 365 ssc = NULL;
278 goto err_ssc; 366 goto err_ssc;
279 } 367 }
280 ssc_p->ssc = ssc; 368 ssc_p->ssc = ssc;
281 369
370 ret = wm8731_i2c_register();
371 if (ret != 0)
372 goto err_ssc;
373
282 at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1); 374 at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
283 if (!at91sam9g20ek_snd_device) { 375 if (!at91sam9g20ek_snd_device) {
284 printk(KERN_DEBUG 376 printk(KERN_ERR "ASoC: Platform device allocation failed\n");
285 "platform device allocation failed\n");
286 ret = -ENOMEM; 377 ret = -ENOMEM;
287 } 378 }
288 379
@@ -292,14 +383,19 @@ static int __init at91sam9g20ek_init(void)
292 383
293 ret = platform_device_add(at91sam9g20ek_snd_device); 384 ret = platform_device_add(at91sam9g20ek_snd_device);
294 if (ret) { 385 if (ret) {
295 printk(KERN_DEBUG 386 printk(KERN_ERR "ASoC: Platform device allocation failed\n");
296 "platform device allocation failed\n");
297 platform_device_put(at91sam9g20ek_snd_device); 387 platform_device_put(at91sam9g20ek_snd_device);
298 } 388 }
299 389
300 return ret; 390 return ret;
301 391
302err_ssc: 392err_ssc:
393 ssc_free(ssc);
394 ssc_p->ssc = NULL;
395err_mclk:
396 clk_put(mclk);
397 mclk = NULL;
398err:
303 return ret; 399 return ret;
304} 400}
305 401
@@ -317,6 +413,8 @@ static void __exit at91sam9g20ek_exit(void)
317 413
318 platform_device_unregister(at91sam9g20ek_snd_device); 414 platform_device_unregister(at91sam9g20ek_snd_device);
319 at91sam9g20ek_snd_device = NULL; 415 at91sam9g20ek_snd_device = NULL;
416 clk_put(mclk);
417 mclk = NULL;
320} 418}
321 419
322module_init(at91sam9g20ek_init); 420module_init(at91sam9g20ek_init);