Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/atmel/sam9g20_wm8731.c
10817 views
1
/*
2
* sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based
3
* ATMEL AT91SAM9G20ek board.
4
*
5
* Copyright (C) 2005 SAN People
6
* Copyright (C) 2008 Atmel
7
*
8
* Authors: Sedji Gaouaou <[email protected]>
9
*
10
* Based on ati_b1_wm8731.c by:
11
* Frank Mandarino <[email protected]>
12
* Copyright 2006 Endrelia Technologies Inc.
13
* Based on corgi.c by:
14
* Copyright 2005 Wolfson Microelectronics PLC.
15
* Copyright 2005 Openedhand Ltd.
16
*
17
* This program is free software; you can redistribute it and/or modify
18
* it under the terms of the GNU General Public License as published by
19
* the Free Software Foundation; either version 2 of the License, or
20
* (at your option) any later version.
21
*
22
* This program is distributed in the hope that it will be useful,
23
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
* GNU General Public License for more details.
26
*
27
* You should have received a copy of the GNU General Public License
28
* along with this program; if not, write to the Free Software
29
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30
*/
31
32
#include <linux/module.h>
33
#include <linux/moduleparam.h>
34
#include <linux/kernel.h>
35
#include <linux/clk.h>
36
#include <linux/timer.h>
37
#include <linux/interrupt.h>
38
#include <linux/platform_device.h>
39
#include <linux/i2c.h>
40
41
#include <linux/atmel-ssc.h>
42
43
#include <sound/core.h>
44
#include <sound/pcm.h>
45
#include <sound/pcm_params.h>
46
#include <sound/soc.h>
47
48
#include <asm/mach-types.h>
49
#include <mach/hardware.h>
50
#include <mach/gpio.h>
51
52
#include "../codecs/wm8731.h"
53
#include "atmel-pcm.h"
54
#include "atmel_ssc_dai.h"
55
56
#define MCLK_RATE 12000000
57
58
/*
59
* As shipped the board does not have inputs. However, it is relatively
60
* straightforward to modify the board to hook them up so support is left
61
* in the driver.
62
*/
63
#undef ENABLE_MIC_INPUT
64
65
static struct clk *mclk;
66
67
static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
68
struct snd_pcm_hw_params *params)
69
{
70
struct snd_soc_pcm_runtime *rtd = substream->private_data;
71
struct snd_soc_dai *codec_dai = rtd->codec_dai;
72
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
73
int ret;
74
75
/* set codec DAI configuration */
76
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
77
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
78
if (ret < 0)
79
return ret;
80
81
/* set cpu DAI configuration */
82
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
83
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
84
if (ret < 0)
85
return ret;
86
87
return 0;
88
}
89
90
static struct snd_soc_ops at91sam9g20ek_ops = {
91
.hw_params = at91sam9g20ek_hw_params,
92
};
93
94
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
95
enum snd_soc_bias_level level)
96
{
97
static int mclk_on;
98
int ret = 0;
99
100
switch (level) {
101
case SND_SOC_BIAS_ON:
102
case SND_SOC_BIAS_PREPARE:
103
if (!mclk_on)
104
ret = clk_enable(mclk);
105
if (ret == 0)
106
mclk_on = 1;
107
break;
108
109
case SND_SOC_BIAS_OFF:
110
case SND_SOC_BIAS_STANDBY:
111
if (mclk_on)
112
clk_disable(mclk);
113
mclk_on = 0;
114
break;
115
}
116
117
return ret;
118
}
119
120
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
121
SND_SOC_DAPM_MIC("Int Mic", NULL),
122
SND_SOC_DAPM_SPK("Ext Spk", NULL),
123
};
124
125
static const struct snd_soc_dapm_route intercon[] = {
126
127
/* speaker connected to LHPOUT */
128
{"Ext Spk", NULL, "LHPOUT"},
129
130
/* mic is connected to Mic Jack, with WM8731 Mic Bias */
131
{"MICIN", NULL, "Mic Bias"},
132
{"Mic Bias", NULL, "Int Mic"},
133
};
134
135
/*
136
* Logic for a wm8731 as connected on a at91sam9g20ek board.
137
*/
138
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
139
{
140
struct snd_soc_codec *codec = rtd->codec;
141
struct snd_soc_dai *codec_dai = rtd->codec_dai;
142
struct snd_soc_dapm_context *dapm = &codec->dapm;
143
int ret;
144
145
printk(KERN_DEBUG
146
"at91sam9g20ek_wm8731 "
147
": at91sam9g20ek_wm8731_init() called\n");
148
149
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
150
MCLK_RATE, SND_SOC_CLOCK_IN);
151
if (ret < 0) {
152
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
153
return ret;
154
}
155
156
/* Add specific widgets */
157
snd_soc_dapm_new_controls(dapm, at91sam9g20ek_dapm_widgets,
158
ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
159
/* Set up specific audio path interconnects */
160
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
161
162
/* not connected */
163
snd_soc_dapm_nc_pin(dapm, "RLINEIN");
164
snd_soc_dapm_nc_pin(dapm, "LLINEIN");
165
166
#ifdef ENABLE_MIC_INPUT
167
snd_soc_dapm_enable_pin(dapm, "Int Mic");
168
#else
169
snd_soc_dapm_nc_pin(dapm, "Int Mic");
170
#endif
171
172
/* always connected */
173
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
174
175
snd_soc_dapm_sync(dapm);
176
177
return 0;
178
}
179
180
static struct snd_soc_dai_link at91sam9g20ek_dai = {
181
.name = "WM8731",
182
.stream_name = "WM8731 PCM",
183
.cpu_dai_name = "atmel-ssc-dai.0",
184
.codec_dai_name = "wm8731-hifi",
185
.init = at91sam9g20ek_wm8731_init,
186
.platform_name = "atmel-pcm-audio",
187
.codec_name = "wm8731.0-001b",
188
.ops = &at91sam9g20ek_ops,
189
};
190
191
static struct snd_soc_card snd_soc_at91sam9g20ek = {
192
.name = "AT91SAMG20-EK",
193
.dai_link = &at91sam9g20ek_dai,
194
.num_links = 1,
195
.set_bias_level = at91sam9g20ek_set_bias_level,
196
};
197
198
static struct platform_device *at91sam9g20ek_snd_device;
199
200
static int __init at91sam9g20ek_init(void)
201
{
202
struct clk *pllb;
203
int ret;
204
205
if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
206
return -ENODEV;
207
208
ret = atmel_ssc_set_audio(0);
209
if (ret != 0) {
210
pr_err("Failed to set SSC 0 for audio: %d\n", ret);
211
return ret;
212
}
213
214
/*
215
* Codec MCLK is supplied by PCK0 - set it up.
216
*/
217
mclk = clk_get(NULL, "pck0");
218
if (IS_ERR(mclk)) {
219
printk(KERN_ERR "ASoC: Failed to get MCLK\n");
220
ret = PTR_ERR(mclk);
221
goto err;
222
}
223
224
pllb = clk_get(NULL, "pllb");
225
if (IS_ERR(pllb)) {
226
printk(KERN_ERR "ASoC: Failed to get PLLB\n");
227
ret = PTR_ERR(pllb);
228
goto err_mclk;
229
}
230
ret = clk_set_parent(mclk, pllb);
231
clk_put(pllb);
232
if (ret != 0) {
233
printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
234
goto err_mclk;
235
}
236
237
clk_set_rate(mclk, MCLK_RATE);
238
239
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
240
if (!at91sam9g20ek_snd_device) {
241
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
242
ret = -ENOMEM;
243
goto err_mclk;
244
}
245
246
platform_set_drvdata(at91sam9g20ek_snd_device,
247
&snd_soc_at91sam9g20ek);
248
249
ret = platform_device_add(at91sam9g20ek_snd_device);
250
if (ret) {
251
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
252
goto err_device_add;
253
}
254
255
return ret;
256
257
err_device_add:
258
platform_device_put(at91sam9g20ek_snd_device);
259
err_mclk:
260
clk_put(mclk);
261
mclk = NULL;
262
err:
263
return ret;
264
}
265
266
static void __exit at91sam9g20ek_exit(void)
267
{
268
platform_device_unregister(at91sam9g20ek_snd_device);
269
at91sam9g20ek_snd_device = NULL;
270
clk_put(mclk);
271
mclk = NULL;
272
}
273
274
module_init(at91sam9g20ek_init);
275
module_exit(at91sam9g20ek_exit);
276
277
/* Module information */
278
MODULE_AUTHOR("Sedji Gaouaou <[email protected]>");
279
MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
280
MODULE_LICENSE("GPL");
281
282