diff options
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap2-git/beagleboard/soc.patch')
-rw-r--r-- | meta/recipes-kernel/linux/linux-omap2-git/beagleboard/soc.patch | 1154 |
1 files changed, 1154 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/soc.patch b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/soc.patch new file mode 100644 index 0000000000..f4cce21ca7 --- /dev/null +++ b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/soc.patch | |||
@@ -0,0 +1,1154 @@ | |||
1 | diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig | ||
2 | index 3903ab7..468536d 100644 | ||
3 | --- a/sound/soc/codecs/Kconfig | ||
4 | +++ b/sound/soc/codecs/Kconfig | ||
5 | @@ -44,3 +44,7 @@ config SND_SOC_CS4270_VD33_ERRATA | ||
6 | config SND_SOC_TLV320AIC3X | ||
7 | tristate | ||
8 | depends on SND_SOC && I2C | ||
9 | + | ||
10 | +config SND_SOC_TWL4030 | ||
11 | + tristate | ||
12 | + depends on SND_SOC && I2C | ||
13 | diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile | ||
14 | index 4e1314c..d2c0b12 100644 | ||
15 | --- a/sound/soc/codecs/Makefile | ||
16 | +++ b/sound/soc/codecs/Makefile | ||
17 | @@ -6,6 +6,7 @@ snd-soc-wm9712-objs := wm9712.o | ||
18 | snd-soc-wm9713-objs := wm9713.o | ||
19 | snd-soc-cs4270-objs := cs4270.o | ||
20 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o | ||
21 | +snd-soc-twl4030-objs := twl4030.o | ||
22 | |||
23 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | ||
24 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | ||
25 | @@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o | ||
26 | obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o | ||
27 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | ||
28 | obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o | ||
29 | +obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o | ||
30 | diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c | ||
31 | new file mode 100644 | ||
32 | index 0000000..eb8370c | ||
33 | --- /dev/null | ||
34 | +++ b/sound/soc/codecs/twl4030.c | ||
35 | @@ -0,0 +1,625 @@ | ||
36 | +/* | ||
37 | + * ALSA SoC TWL4030 codec driver | ||
38 | + * | ||
39 | + * Author: Steve Sakoman, <steve@sakoman.com> | ||
40 | + * | ||
41 | + * This program is free software; you can redistribute it and/or modify | ||
42 | + * it under the terms of the GNU General Public License version 2 as | ||
43 | + * published by the Free Software Foundation. | ||
44 | + */ | ||
45 | + | ||
46 | +#include <linux/module.h> | ||
47 | +#include <linux/moduleparam.h> | ||
48 | +#include <linux/init.h> | ||
49 | +#include <linux/delay.h> | ||
50 | +#include <linux/pm.h> | ||
51 | +#include <linux/i2c.h> | ||
52 | +#include <linux/platform_device.h> | ||
53 | +#include <linux/i2c/twl4030.h> | ||
54 | +#include <sound/core.h> | ||
55 | +#include <sound/pcm.h> | ||
56 | +#include <sound/pcm_params.h> | ||
57 | +#include <sound/soc.h> | ||
58 | +#include <sound/soc-dapm.h> | ||
59 | +#include <sound/initval.h> | ||
60 | + | ||
61 | +#include "twl4030.h" | ||
62 | + | ||
63 | +/* | ||
64 | + * twl4030 register cache & default register settings | ||
65 | + */ | ||
66 | +static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | ||
67 | + 0x00, // this register not used | ||
68 | + 0x93, // REG_CODEC_MODE (0x1) | ||
69 | + 0xc3, // REG_OPTION (0x2) | ||
70 | + 0x00, // REG_UNKNOWN (0x3) | ||
71 | + 0x00, // REG_MICBIAS_CTL (0x4) | ||
72 | + 0x34, // REG_ANAMICL (0x5) | ||
73 | + 0x14, // REG_ANAMICR (0x6) | ||
74 | + 0x0a, // REG_AVADC_CTL (0x7) | ||
75 | + 0x00, // REG_ADCMICSEL (0x8) | ||
76 | + 0x00, // REG_DIGMIXING (0x9) | ||
77 | + 0x0c, // REG_ATXL1PGA (0xA) | ||
78 | + 0x0c, // REG_ATXR1PGA (0xB) | ||
79 | + 0x00, // REG_AVTXL2PGA (0xC) | ||
80 | + 0x00, // REG_AVTXR2PGA (0xD) | ||
81 | + 0x01, // REG_AUDIO_IF (0xE) | ||
82 | + 0x00, // REG_VOICE_IF (0xF) | ||
83 | + 0x00, // REG_ARXR1PGA (0x10) | ||
84 | + 0x00, // REG_ARXL1PGA (0x11) | ||
85 | + 0x6c, // REG_ARXR2PGA (0x12) | ||
86 | + 0x6c, // REG_ARXL2PGA (0x13) | ||
87 | + 0x00, // REG_VRXPGA (0x14) | ||
88 | + 0x00, // REG_VSTPGA (0x15) | ||
89 | + 0x00, // REG_VRX2ARXPGA (0x16) | ||
90 | + 0x0c, // REG_AVDAC_CTL (0x17) | ||
91 | + 0x00, // REG_ARX2VTXPGA (0x18) | ||
92 | + 0x00, // REG_ARXL1_APGA_CTL (0x19) | ||
93 | + 0x00, // REG_ARXR1_APGA_CTL (0x1A) | ||
94 | + 0x4b, // REG_ARXL2_APGA_CTL (0x1B) | ||
95 | + 0x4b, // REG_ARXR2_APGA_CTL (0x1C) | ||
96 | + 0x00, // REG_ATX2ARXPGA (0x1D) | ||
97 | + 0x00, // REG_BT_IF (0x1E) | ||
98 | + 0x00, // REG_BTPGA (0x1F) | ||
99 | + 0x00, // REG_BTSTPGA (0x20) | ||
100 | + 0x00, // REG_EAR_CTL (0x21) | ||
101 | + 0x24, // REG_HS_SEL (0x22) | ||
102 | + 0x0a, // REG_HS_GAIN_SET (0x23) | ||
103 | + 0x00, // REG_HS_POPN_SET (0x24) | ||
104 | + 0x00, // REG_PREDL_CTL (0x25) | ||
105 | + 0x00, // REG_PREDR_CTL (0x26) | ||
106 | + 0x00, // REG_PRECKL_CTL (0x27) | ||
107 | + 0x00, // REG_PRECKR_CTL (0x28) | ||
108 | + 0x00, // REG_HFL_CTL (0x29) | ||
109 | + 0x00, // REG_HFR_CTL (0x2A) | ||
110 | + 0x00, // REG_ALC_CTL (0x2B) | ||
111 | + 0x00, // REG_ALC_SET1 (0x2C) | ||
112 | + 0x00, // REG_ALC_SET2 (0x2D) | ||
113 | + 0x00, // REG_BOOST_CTL (0x2E) | ||
114 | + 0x01, // REG_SOFTVOL_CTL (0x2F) | ||
115 | + 0x00, // REG_DTMF_FREQSEL (0x30) | ||
116 | + 0x00, // REG_DTMF_TONEXT1H (0x31) | ||
117 | + 0x00, // REG_DTMF_TONEXT1L (0x32) | ||
118 | + 0x00, // REG_DTMF_TONEXT2H (0x33) | ||
119 | + 0x00, // REG_DTMF_TONEXT2L (0x34) | ||
120 | + 0x00, // REG_DTMF_TONOFF (0x35) | ||
121 | + 0x00, // REG_DTMF_WANONOFF (0x36) | ||
122 | + 0x00, // REG_I2S_RX_SCRAMBLE_H (0x37) | ||
123 | + 0x00, // REG_I2S_RX_SCRAMBLE_M (0x38) | ||
124 | + 0x00, // REG_I2S_RX_SCRAMBLE_L (0x39) | ||
125 | + 0x16, // REG_APLL_CTL (0x3A) | ||
126 | + 0x00, // REG_DTMF_CTL (0x3B) | ||
127 | + 0x00, // REG_DTMF_PGA_CTL2 (0x3C) | ||
128 | + 0x00, // REG_DTMF_PGA_CTL1 (0x3D) | ||
129 | + 0x00, // REG_MISC_SET_1 (0x3E) | ||
130 | + 0x00, // REG_PCMBTMUX (0x3F) | ||
131 | + 0x00, // REG_RX_PATH_SEL (0x43) | ||
132 | + 0x00, // REG_VDL_APGA_CTL (0x44) | ||
133 | + 0x00, // REG_VIBRA_CTL (0x45) | ||
134 | + 0x00, // REG_VIBRA_SET (0x46) | ||
135 | + 0x00, // REG_VIBRA_PWM_SET (0x47) | ||
136 | + 0x00, // REG_ANAMIC_GAIN (0x48) | ||
137 | + 0x00, // REG_MISC_SET_2 (0x49) | ||
138 | +}; | ||
139 | + | ||
140 | +static void twl4030_dump_registers(void) | ||
141 | +{ | ||
142 | + int i = 0; | ||
143 | + u8 data; | ||
144 | + | ||
145 | + printk(KERN_INFO "TWL 4030 Register dump for Audio Module\n"); | ||
146 | + | ||
147 | + for (i = REG_CODEC_MODE; i <= REG_MISC_SET_2; i++) { | ||
148 | + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &data, i); | ||
149 | + printk(KERN_INFO "Register[0x%02x]=0x%02x\n", i, data); | ||
150 | + } | ||
151 | +} | ||
152 | + | ||
153 | +struct twl4030_priv { | ||
154 | + unsigned int dummy; | ||
155 | +}; | ||
156 | + | ||
157 | +/* | ||
158 | + * read twl4030 register cache | ||
159 | + */ | ||
160 | +static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec, | ||
161 | + unsigned int reg) | ||
162 | +{ | ||
163 | + u8 *cache = codec->reg_cache; | ||
164 | + | ||
165 | + return cache[reg]; | ||
166 | +} | ||
167 | + | ||
168 | +/* | ||
169 | + * write twl4030 register cache | ||
170 | + */ | ||
171 | +static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec, | ||
172 | + u8 reg, u8 value) | ||
173 | +{ | ||
174 | + u8 *cache = codec->reg_cache; | ||
175 | + | ||
176 | + if (reg >= TWL4030_CACHEREGNUM) | ||
177 | + return; | ||
178 | + cache[reg] = value; | ||
179 | +} | ||
180 | + | ||
181 | +/* | ||
182 | + * write to the twl4030 register space | ||
183 | + */ | ||
184 | +static int twl4030_write(struct snd_soc_codec *codec, | ||
185 | + unsigned int reg, unsigned int value) | ||
186 | +{ | ||
187 | + twl4030_write_reg_cache(codec, reg, value); | ||
188 | + return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); | ||
189 | +} | ||
190 | + | ||
191 | +static void twl4030_init_chip(void) | ||
192 | +{ | ||
193 | + unsigned char byte; | ||
194 | + int i; | ||
195 | + | ||
196 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
197 | + twl4030_reg[REG_CODEC_MODE] & 0xfd, REG_CODEC_MODE); | ||
198 | + | ||
199 | + udelay(10); /* delay for power settling */ | ||
200 | + | ||
201 | + for (i = REG_OPTION; i <= REG_MISC_SET_2; i++) { | ||
202 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, twl4030_reg[i], i); | ||
203 | + } | ||
204 | + | ||
205 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
206 | + twl4030_reg[REG_CODEC_MODE], REG_CODEC_MODE); | ||
207 | + | ||
208 | + udelay(10); /* delay for power settling */ | ||
209 | + | ||
210 | + /* initiate offset cancellation */ | ||
211 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
212 | + twl4030_reg[REG_ANAMICL] | 0x80, REG_ANAMICL); | ||
213 | + | ||
214 | + /* wait for offset cancellation to complete */ | ||
215 | + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, REG_ANAMICL); | ||
216 | + while ((byte & 0x80) == 0x80) | ||
217 | + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, REG_ANAMICL); | ||
218 | + | ||
219 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | ||
220 | + twl4030_reg[REG_MISC_SET_1] | 0x02, REG_MISC_SET_1); | ||
221 | + | ||
222 | +} | ||
223 | + | ||
224 | +static const struct snd_kcontrol_new twl4030_snd_controls[] = { | ||
225 | + SOC_DOUBLE_R("Master Playback Volume", | ||
226 | + REG_ARXL2PGA, REG_ARXR2PGA, | ||
227 | + 0, 127, 0), | ||
228 | + SOC_DOUBLE_R("Capture Volume", | ||
229 | + REG_ATXL1PGA, REG_ATXR1PGA, | ||
230 | + 0, 127, 0), | ||
231 | +}; | ||
232 | + | ||
233 | +/* add non dapm controls */ | ||
234 | +static int twl4030_add_controls(struct snd_soc_codec *codec) | ||
235 | +{ | ||
236 | + int err, i; | ||
237 | + | ||
238 | + for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) { | ||
239 | + err = snd_ctl_add(codec->card, | ||
240 | + snd_soc_cnew(&twl4030_snd_controls[i], | ||
241 | + codec, NULL)); | ||
242 | + if (err < 0) | ||
243 | + return err; | ||
244 | + } | ||
245 | + | ||
246 | + return 0; | ||
247 | +} | ||
248 | + | ||
249 | +static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | ||
250 | + SND_SOC_DAPM_INPUT("INL"), | ||
251 | + SND_SOC_DAPM_INPUT("INR"), | ||
252 | + | ||
253 | + SND_SOC_DAPM_OUTPUT("OUTL"), | ||
254 | + SND_SOC_DAPM_OUTPUT("OUTR"), | ||
255 | + | ||
256 | + SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0), | ||
257 | + SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0), | ||
258 | + | ||
259 | + SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0), | ||
260 | + SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0), | ||
261 | +}; | ||
262 | + | ||
263 | +static const char *intercon[][3] = { | ||
264 | + /* outputs */ | ||
265 | + {"OUTL", NULL, "DACL"}, | ||
266 | + {"OUTR", NULL, "DACR"}, | ||
267 | + | ||
268 | + /* inputs */ | ||
269 | + {"ADCL", NULL, "INL"}, | ||
270 | + {"ADCR", NULL, "INR"}, | ||
271 | + | ||
272 | + /* terminator */ | ||
273 | + {NULL, NULL, NULL}, | ||
274 | +}; | ||
275 | + | ||
276 | +static int twl4030_add_widgets(struct snd_soc_codec *codec) | ||
277 | +{ | ||
278 | + int i; | ||
279 | + | ||
280 | + for (i = 0; i < ARRAY_SIZE(twl4030_dapm_widgets); i++) | ||
281 | + snd_soc_dapm_new_control(codec, &twl4030_dapm_widgets[i]); | ||
282 | + | ||
283 | + /* set up audio path interconnects */ | ||
284 | + for (i = 0; intercon[i][0] != NULL; i++) | ||
285 | + snd_soc_dapm_connect_input(codec, intercon[i][0], | ||
286 | + intercon[i][1], intercon[i][2]); | ||
287 | + | ||
288 | + snd_soc_dapm_new_widgets(codec); | ||
289 | + return 0; | ||
290 | +} | ||
291 | + | ||
292 | +static int twl4030_dapm_event(struct snd_soc_codec *codec, int event) | ||
293 | +{ | ||
294 | + | ||
295 | + printk(KERN_INFO "TWL4030 Audio Codec dapm event\n"); | ||
296 | + switch (event) { | ||
297 | + case SNDRV_CTL_POWER_D0: /* full On */ | ||
298 | + break; | ||
299 | + case SNDRV_CTL_POWER_D1: /* partial On */ | ||
300 | + case SNDRV_CTL_POWER_D2: /* partial On */ | ||
301 | + break; | ||
302 | + case SNDRV_CTL_POWER_D3hot: /* off, with power */ | ||
303 | + break; | ||
304 | + case SNDRV_CTL_POWER_D3cold: /* off, without power */ | ||
305 | + break; | ||
306 | + } | ||
307 | + codec->dapm_state = event; | ||
308 | + | ||
309 | + return 0; | ||
310 | +} | ||
311 | + | ||
312 | +static void twl4030_power_up (struct snd_soc_codec *codec, u8 mode) | ||
313 | +{ | ||
314 | + u8 popn, hsgain; | ||
315 | + | ||
316 | + twl4030_write(codec, REG_CODEC_MODE, mode & ~CODECPDZ); | ||
317 | + twl4030_write(codec, REG_CODEC_MODE, mode | CODECPDZ); | ||
318 | + udelay(10); | ||
319 | + | ||
320 | + popn = twl4030_read_reg_cache(codec, REG_HS_POPN_SET); | ||
321 | + popn &= RAMP_DELAY; | ||
322 | + popn |= VMID_EN | RAMP_DELAY_161MS; | ||
323 | + twl4030_write(codec, REG_HS_POPN_SET, popn); | ||
324 | + | ||
325 | + hsgain = HSR_GAIN_0DB| HSL_GAIN_0DB; | ||
326 | + twl4030_write(codec, REG_HS_GAIN_SET, hsgain); | ||
327 | + | ||
328 | + popn |= RAMP_EN; | ||
329 | + twl4030_write(codec, REG_HS_POPN_SET, popn); | ||
330 | +} | ||
331 | + | ||
332 | +static void twl4030_power_down (struct snd_soc_codec *codec) | ||
333 | +{ | ||
334 | + u8 popn, hsgain, mode; | ||
335 | + | ||
336 | + popn = twl4030_read_reg_cache(codec, REG_HS_POPN_SET); | ||
337 | + popn &= ~RAMP_EN; | ||
338 | + twl4030_write(codec, REG_HS_POPN_SET, popn); | ||
339 | + | ||
340 | + hsgain = HSR_GAIN_PWR_DOWN | HSL_GAIN_PWR_DOWN; | ||
341 | + twl4030_write(codec, REG_HS_GAIN_SET, hsgain); | ||
342 | + | ||
343 | + popn &= ~VMID_EN; | ||
344 | + twl4030_write(codec, REG_HS_POPN_SET, popn); | ||
345 | + | ||
346 | + mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE); | ||
347 | + mode &= ~CODECPDZ; | ||
348 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
349 | + udelay(10); | ||
350 | +} | ||
351 | + | ||
352 | + | ||
353 | +static int twl4030_hw_params(struct snd_pcm_substream *substream, | ||
354 | + struct snd_pcm_hw_params *params) | ||
355 | +{ | ||
356 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
357 | + struct snd_soc_device *socdev = rtd->socdev; | ||
358 | + struct snd_soc_codec *codec = socdev->codec; | ||
359 | + struct twl4030_priv *twl4030 = codec->private_data; | ||
360 | + u8 mode, old_mode, format, old_format; | ||
361 | + | ||
362 | + | ||
363 | + /* bit rate */ | ||
364 | + old_mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE) & ~CODECPDZ; | ||
365 | + mode = old_mode; | ||
366 | + mode &= ~APLL_RATE; | ||
367 | + switch (params_rate(params)) { | ||
368 | + case 44100: | ||
369 | + mode |= APLL_RATE_44100; | ||
370 | + break; | ||
371 | + case 48000: | ||
372 | + mode |= APLL_RATE_48000; | ||
373 | + break; | ||
374 | + default: | ||
375 | + printk(KERN_INFO "TWL4030 hw params: unknown rate %d\n", params_rate(params)); | ||
376 | + return -EINVAL; | ||
377 | + } | ||
378 | + | ||
379 | + if (mode != old_mode) { | ||
380 | + /* change rate and turn codec back on */ | ||
381 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
382 | + mode |= CODECPDZ; | ||
383 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
384 | + } | ||
385 | + | ||
386 | + /* sample size */ | ||
387 | + old_format = twl4030_read_reg_cache(codec, REG_AUDIO_IF); | ||
388 | + format = old_format; | ||
389 | + format &= ~DATA_WIDTH; | ||
390 | + switch (params_format(params)) { | ||
391 | + case SNDRV_PCM_FORMAT_S16_LE: | ||
392 | + format |= DATA_WIDTH_16S_16W; | ||
393 | + break; | ||
394 | + case SNDRV_PCM_FORMAT_S24_LE: | ||
395 | + format |= DATA_WIDTH_32S_24W; | ||
396 | + break; | ||
397 | + default: | ||
398 | + printk(KERN_INFO "TWL4030 hw params: unknown format %d\n", params_format(params)); | ||
399 | + return -EINVAL; | ||
400 | + } | ||
401 | + | ||
402 | + if (format != old_format) { | ||
403 | + | ||
404 | + /* turn off codec before changing format */ | ||
405 | + mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE); | ||
406 | + mode &= ~CODECPDZ; | ||
407 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
408 | + | ||
409 | + /* change format */ | ||
410 | + twl4030_write(codec, REG_AUDIO_IF, format); | ||
411 | + | ||
412 | + /* turn on codec */ | ||
413 | + mode |= CODECPDZ; | ||
414 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
415 | + } | ||
416 | + return 0; | ||
417 | +} | ||
418 | + | ||
419 | +static int twl4030_mute(struct snd_soc_codec_dai *dai, int mute) | ||
420 | +{ | ||
421 | + struct snd_soc_codec *codec = dai->codec; | ||
422 | + | ||
423 | + u8 ldac_reg = twl4030_read_reg_cache(codec, REG_ARXL2PGA); | ||
424 | + u8 rdac_reg = twl4030_read_reg_cache(codec, REG_ARXR2PGA); | ||
425 | + | ||
426 | + if (mute) { | ||
427 | + /* printk(KERN_INFO "TWL4030 Audio Codec mute\n"); */ | ||
428 | + twl4030_write(codec, REG_ARXL2PGA, 0x00); | ||
429 | + twl4030_write(codec, REG_ARXR2PGA, 0x00); | ||
430 | + twl4030_write_reg_cache(codec, REG_ARXL2PGA, ldac_reg); | ||
431 | + twl4030_write_reg_cache(codec, REG_ARXR2PGA, rdac_reg); | ||
432 | + } | ||
433 | + else { | ||
434 | + /* printk(KERN_INFO "TWL4030 Audio Codec unmute: %02x/%02x\n", ldac_reg, rdac_reg); */ | ||
435 | + twl4030_write(codec, REG_ARXL2PGA, ldac_reg); | ||
436 | + twl4030_write(codec, REG_ARXR2PGA, rdac_reg); | ||
437 | + } | ||
438 | + | ||
439 | + return 0; | ||
440 | +} | ||
441 | + | ||
442 | +static int twl4030_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, | ||
443 | + unsigned int fmt) | ||
444 | +{ | ||
445 | + struct snd_soc_codec *codec = codec_dai->codec; | ||
446 | + struct twl4030_priv *twl4030 = codec->private_data; | ||
447 | + u8 mode, old_format, format; | ||
448 | + | ||
449 | + /* get format */ | ||
450 | + old_format = twl4030_read_reg_cache(codec, REG_AUDIO_IF); | ||
451 | + format = old_format; | ||
452 | + | ||
453 | + /* set master/slave audio interface */ | ||
454 | + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
455 | + case SND_SOC_DAIFMT_CBM_CFM: | ||
456 | + /* printk(KERN_INFO "TWL4030 set dai fmt: master\n"); */ | ||
457 | + format &= ~(AIF_SLAVE_EN); | ||
458 | + format |= CLK256FS_EN; | ||
459 | + break; | ||
460 | + case SND_SOC_DAIFMT_CBS_CFS: | ||
461 | + /* printk(KERN_INFO "TWL4030 set dai fmt: slave\n"); */ | ||
462 | + format &= ~(CLK256FS_EN); | ||
463 | + format |= AIF_SLAVE_EN; | ||
464 | + break; | ||
465 | + default: | ||
466 | + return -EINVAL; | ||
467 | + } | ||
468 | + | ||
469 | + /* interface format */ | ||
470 | + format &= ~AIF_FORMAT; | ||
471 | + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
472 | + case SND_SOC_DAIFMT_I2S: | ||
473 | + /* printk(KERN_INFO "TWL4030 set dai fmt: i2s\n"); */ | ||
474 | + format |= AIF_FORMAT_CODEC; | ||
475 | + break; | ||
476 | + default: | ||
477 | + return -EINVAL; | ||
478 | + } | ||
479 | + | ||
480 | + if (format != old_format) { | ||
481 | + | ||
482 | + /* turn off codec before changing format */ | ||
483 | + mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE); | ||
484 | + mode &= ~CODECPDZ; | ||
485 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
486 | + | ||
487 | + /* change format */ | ||
488 | + twl4030_write(codec, REG_AUDIO_IF, format); | ||
489 | + | ||
490 | + mode |= CODECPDZ; | ||
491 | + twl4030_write(codec, REG_CODEC_MODE, mode); | ||
492 | + } | ||
493 | + | ||
494 | + return 0; | ||
495 | +} | ||
496 | + | ||
497 | +#define TWL4030_RATES SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | ||
498 | +#define TWL4030_FORMATS SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE | ||
499 | + | ||
500 | +struct snd_soc_codec_dai twl4030_dai = { | ||
501 | + .name = "twl4030", | ||
502 | + .playback = { | ||
503 | + .stream_name = "Playback", | ||
504 | + .channels_min = 2, | ||
505 | + .channels_max = 2, | ||
506 | + .rates = TWL4030_RATES, | ||
507 | + .formats = TWL4030_FORMATS,}, | ||
508 | + .capture = { | ||
509 | + .stream_name = "Capture", | ||
510 | + .channels_min = 2, | ||
511 | + .channels_max = 2, | ||
512 | + .rates = TWL4030_RATES, | ||
513 | + .formats = TWL4030_FORMATS,}, | ||
514 | + .ops = { | ||
515 | + .hw_params = twl4030_hw_params, | ||
516 | + }, | ||
517 | + .dai_ops = { | ||
518 | + .digital_mute = twl4030_mute, | ||
519 | + .set_fmt = twl4030_set_dai_fmt, | ||
520 | + } | ||
521 | +}; | ||
522 | + | ||
523 | +EXPORT_SYMBOL_GPL(twl4030_dai); | ||
524 | + | ||
525 | +static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | ||
526 | +{ | ||
527 | + struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
528 | + struct snd_soc_codec *codec = socdev->codec; | ||
529 | + | ||
530 | + printk(KERN_INFO "TWL4030 Audio Codec suspend\n"); | ||
531 | + twl4030_dapm_event(codec, SNDRV_CTL_POWER_D3cold); | ||
532 | + | ||
533 | + return 0; | ||
534 | +} | ||
535 | + | ||
536 | +static int twl4030_resume(struct platform_device *pdev) | ||
537 | +{ | ||
538 | + struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
539 | + struct snd_soc_codec *codec = socdev->codec; | ||
540 | + int i; | ||
541 | + u16 *cache = codec->reg_cache; | ||
542 | + | ||
543 | + printk(KERN_INFO "TWL4030 Audio Codec resume\n"); | ||
544 | + /* Sync reg_cache with the hardware */ | ||
545 | + for (i = REG_CODEC_MODE; i <= REG_MISC_SET_2; i++) { | ||
546 | + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, cache[i], i); | ||
547 | + } | ||
548 | + twl4030_dapm_event(codec, SNDRV_CTL_POWER_D3hot); | ||
549 | + twl4030_dapm_event(codec, codec->suspend_dapm_state); | ||
550 | + return 0; | ||
551 | +} | ||
552 | + | ||
553 | +/* | ||
554 | + * initialize the driver | ||
555 | + * register the mixer and dsp interfaces with the kernel | ||
556 | + */ | ||
557 | + | ||
558 | +static int twl4030_init(struct snd_soc_device *socdev) | ||
559 | +{ | ||
560 | + struct snd_soc_codec *codec = socdev->codec; | ||
561 | + int ret = 0; | ||
562 | + | ||
563 | + printk(KERN_INFO "TWL4030 Audio Codec init \n"); | ||
564 | + | ||
565 | + codec->name = "twl4030"; | ||
566 | + codec->owner = THIS_MODULE; | ||
567 | + codec->read = twl4030_read_reg_cache; | ||
568 | + codec->write = twl4030_write; | ||
569 | + codec->dapm_event = twl4030_dapm_event; | ||
570 | + codec->dai = &twl4030_dai; | ||
571 | + codec->num_dai = 1; | ||
572 | + codec->reg_cache_size = sizeof(twl4030_reg); | ||
573 | + codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), GFP_KERNEL); | ||
574 | + if (codec->reg_cache == NULL) | ||
575 | + return -ENOMEM; | ||
576 | + | ||
577 | + /* register pcms */ | ||
578 | + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
579 | + if (ret < 0) { | ||
580 | + printk(KERN_ERR "twl4030: failed to create pcms\n"); | ||
581 | + goto pcm_err; | ||
582 | + } | ||
583 | + | ||
584 | + twl4030_add_controls(codec); | ||
585 | + twl4030_add_widgets(codec); | ||
586 | + | ||
587 | + ret = snd_soc_register_card(socdev); | ||
588 | + if (ret < 0) { | ||
589 | + printk(KERN_ERR "twl4030: failed to register card\n"); | ||
590 | + goto card_err; | ||
591 | + } | ||
592 | + | ||
593 | + twl4030_init_chip(); | ||
594 | + twl4030_power_up(codec, APLL_RATE_44100 | OPT_MODE); | ||
595 | + | ||
596 | + return ret; | ||
597 | + | ||
598 | +card_err: | ||
599 | + printk(KERN_INFO "TWL4030 Audio Codec init card error\n"); | ||
600 | + snd_soc_free_pcms(socdev); | ||
601 | + snd_soc_dapm_free(socdev); | ||
602 | +pcm_err: | ||
603 | + printk(KERN_INFO "TWL4030 Audio Codec init pcm error\n"); | ||
604 | + kfree(codec->reg_cache); | ||
605 | + return ret; | ||
606 | +} | ||
607 | + | ||
608 | +static struct snd_soc_device *twl4030_socdev; | ||
609 | + | ||
610 | +static int twl4030_probe(struct platform_device *pdev) | ||
611 | +{ | ||
612 | + struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
613 | + struct snd_soc_codec *codec; | ||
614 | + struct twl4030_priv *twl4030; | ||
615 | + | ||
616 | + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
617 | + if (codec == NULL) | ||
618 | + return -ENOMEM; | ||
619 | + | ||
620 | + twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); | ||
621 | + if (twl4030 == NULL) { | ||
622 | + kfree(codec); | ||
623 | + return -ENOMEM; | ||
624 | + } | ||
625 | + | ||
626 | + codec->private_data = twl4030; | ||
627 | + socdev->codec = codec; | ||
628 | + mutex_init(&codec->mutex); | ||
629 | + INIT_LIST_HEAD(&codec->dapm_widgets); | ||
630 | + INIT_LIST_HEAD(&codec->dapm_paths); | ||
631 | + | ||
632 | + twl4030_socdev = socdev; | ||
633 | + twl4030_init(socdev); | ||
634 | + | ||
635 | + return 0; | ||
636 | +} | ||
637 | + | ||
638 | +static int twl4030_remove(struct platform_device *pdev) | ||
639 | +{ | ||
640 | + struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
641 | + struct snd_soc_codec *codec = socdev->codec; | ||
642 | + | ||
643 | + printk(KERN_INFO "TWL4030 Audio Codec remove\n"); | ||
644 | + kfree(codec->private_data); | ||
645 | + kfree(codec); | ||
646 | + | ||
647 | + return 0; | ||
648 | +} | ||
649 | + | ||
650 | +struct snd_soc_codec_device soc_codec_dev_twl4030 = { | ||
651 | + .probe = twl4030_probe, | ||
652 | + .remove = twl4030_remove, | ||
653 | + .suspend = twl4030_suspend, | ||
654 | + .resume = twl4030_resume, | ||
655 | +}; | ||
656 | +EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | ||
657 | + | ||
658 | +MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); | ||
659 | +MODULE_AUTHOR("Steve Sakoman"); | ||
660 | +MODULE_LICENSE("GPL"); | ||
661 | diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h | ||
662 | new file mode 100644 | ||
663 | index 0000000..e126f96 | ||
664 | --- /dev/null | ||
665 | +++ b/sound/soc/codecs/twl4030.h | ||
666 | @@ -0,0 +1,152 @@ | ||
667 | +/* | ||
668 | + * ALSA SoC TWL4030 codec driver | ||
669 | + * | ||
670 | + * Author: Steve Sakoman, <steve@sakoman.com> | ||
671 | + * | ||
672 | + * This program is free software; you can redistribute it and/or modify | ||
673 | + * it under the terms of the GNU General Public License version 2 as | ||
674 | + * published by the Free Software Foundation. | ||
675 | + */ | ||
676 | + | ||
677 | +#ifndef __TWL4030_AUDIO_H__ | ||
678 | +#define __TWL4030_AUDIO_H__ | ||
679 | + | ||
680 | +#define REG_CODEC_MODE 0x1 | ||
681 | +#define REG_OPTION 0x2 | ||
682 | +#define REG_UNKNOWN 0x3 | ||
683 | +#define REG_MICBIAS_CTL 0x4 | ||
684 | +#define REG_ANAMICL 0x5 | ||
685 | +#define REG_ANAMICR 0x6 | ||
686 | +#define REG_AVADC_CTL 0x7 | ||
687 | +#define REG_ADCMICSEL 0x8 | ||
688 | +#define REG_DIGMIXING 0x9 | ||
689 | +#define REG_ATXL1PGA 0xA | ||
690 | +#define REG_ATXR1PGA 0xB | ||
691 | +#define REG_AVTXL2PGA 0xC | ||
692 | +#define REG_AVTXR2PGA 0xD | ||
693 | +#define REG_AUDIO_IF 0xE | ||
694 | +#define REG_VOICE_IF 0xF | ||
695 | +#define REG_ARXR1PGA 0x10 | ||
696 | +#define REG_ARXL1PGA 0x11 | ||
697 | +#define REG_ARXR2PGA 0x12 | ||
698 | +#define REG_ARXL2PGA 0x13 | ||
699 | +#define REG_VRXPGA 0x14 | ||
700 | +#define REG_VSTPGA 0x15 | ||
701 | +#define REG_VRX2ARXPGA 0x16 | ||
702 | +#define REG_AVDAC_CTL 0x17 | ||
703 | +#define REG_ARX2VTXPGA 0x18 | ||
704 | +#define REG_ARXL1_APGA_CTL 0x19 | ||
705 | +#define REG_ARXR1_APGA_CTL 0x1A | ||
706 | +#define REG_ARXL2_APGA_CTL 0x1B | ||
707 | +#define REG_ARXR2_APGA_CTL 0x1C | ||
708 | +#define REG_ATX2ARXPGA 0x1D | ||
709 | +#define REG_BT_IF 0x1E | ||
710 | +#define REG_BTPGA 0x1F | ||
711 | +#define REG_BTSTPGA 0x20 | ||
712 | +#define REG_EAR_CTL 0x21 | ||
713 | +#define REG_HS_SEL 0x22 | ||
714 | +#define REG_HS_GAIN_SET 0x23 | ||
715 | +#define REG_HS_POPN_SET 0x24 | ||
716 | +#define REG_PREDL_CTL 0x25 | ||
717 | +#define REG_PREDR_CTL 0x26 | ||
718 | +#define REG_PRECKL_CTL 0x27 | ||
719 | +#define REG_PRECKR_CTL 0x28 | ||
720 | +#define REG_HFL_CTL 0x29 | ||
721 | +#define REG_HFR_CTL 0x2A | ||
722 | +#define REG_ALC_CTL 0x2B | ||
723 | +#define REG_ALC_SET1 0x2C | ||
724 | +#define REG_ALC_SET2 0x2D | ||
725 | +#define REG_BOOST_CTL 0x2E | ||
726 | +#define REG_SOFTVOL_CTL 0x2F | ||
727 | +#define REG_DTMF_FREQSEL 0x30 | ||
728 | +#define REG_DTMF_TONEXT1H 0x31 | ||
729 | +#define REG_DTMF_TONEXT1L 0x32 | ||
730 | +#define REG_DTMF_TONEXT2H 0x33 | ||
731 | +#define REG_DTMF_TONEXT2L 0x34 | ||
732 | +#define REG_DTMF_TONOFF 0x35 | ||
733 | +#define REG_DTMF_WANONOFF 0x36 | ||
734 | +#define REG_I2S_RX_SCRAMBLE_H 0x37 | ||
735 | +#define REG_I2S_RX_SCRAMBLE_M 0x38 | ||
736 | +#define REG_I2S_RX_SCRAMBLE_L 0x39 | ||
737 | +#define REG_APLL_CTL 0x3A | ||
738 | +#define REG_DTMF_CTL 0x3B | ||
739 | +#define REG_DTMF_PGA_CTL2 0x3C | ||
740 | +#define REG_DTMF_PGA_CTL1 0x3D | ||
741 | +#define REG_MISC_SET_1 0x3E | ||
742 | +#define REG_PCMBTMUX 0x3F | ||
743 | +#define REG_RX_PATH_SEL 0x43 | ||
744 | +#define REG_VDL_APGA_CTL 0x44 | ||
745 | +#define REG_VIBRA_CTL 0x45 | ||
746 | +#define REG_VIBRA_SET 0x46 | ||
747 | +#define REG_VIBRA_PWM_SET 0x47 | ||
748 | +#define REG_ANAMIC_GAIN 0x48 | ||
749 | +#define REG_MISC_SET_2 0x49 | ||
750 | + | ||
751 | +#define TWL4030_CACHEREGNUM REG_MISC_SET_2 + 1 | ||
752 | + | ||
753 | +/* Bitfield Definitions */ | ||
754 | + | ||
755 | +/* CODEC_MODE (0x01) Fields */ | ||
756 | + | ||
757 | +#define APLL_RATE 0xF0 | ||
758 | +#define APLL_RATE_8000 0x00 | ||
759 | +#define APLL_RATE_11025 0x10 | ||
760 | +#define APLL_RATE_12000 0x20 | ||
761 | +#define APLL_RATE_16000 0x40 | ||
762 | +#define APLL_RATE_22050 0x50 | ||
763 | +#define APLL_RATE_24000 0x60 | ||
764 | +#define APLL_RATE_32000 0x80 | ||
765 | +#define APLL_RATE_44100 0x90 | ||
766 | +#define APLL_RATE_48000 0xa0 | ||
767 | +#define SEL_16K 0x04 | ||
768 | +#define CODECPDZ 0x02 | ||
769 | +#define OPT_MODE 0x01 | ||
770 | + | ||
771 | +/* AUDIO_IF (0x0E) Fields */ | ||
772 | + | ||
773 | +#define AIF_SLAVE_EN 0x80 | ||
774 | +#define DATA_WIDTH 0x60 | ||
775 | +#define DATA_WIDTH_16S_16W 0x00 | ||
776 | +#define DATA_WIDTH_32S_16W 0x40 | ||
777 | +#define DATA_WIDTH_32S_24W 0x60 | ||
778 | +#define AIF_FORMAT 0x18 | ||
779 | +#define AIF_FORMAT_CODEC 0x00 | ||
780 | +#define AIF_FORMAT_LEFT 0x08 | ||
781 | +#define AIF_FORMAT_RIGHT 0x10 | ||
782 | +#define AIF_FORMAT_TDM 0x18 | ||
783 | +#define AIF_TRI_EN 0x04 | ||
784 | +#define CLK256FS_EN 0x02 | ||
785 | +#define AIF_EN 0x01 | ||
786 | + | ||
787 | +/* HS_GAIN_SET (0x23) Fields */ | ||
788 | + | ||
789 | +#define HSR_GAIN 0x0c | ||
790 | +#define HSR_GAIN_PWR_DOWN 0x00 | ||
791 | +#define HSR_GAIN_PLUS_6DB 0x04 | ||
792 | +#define HSR_GAIN_0DB 0x08 | ||
793 | +#define HSR_GAIN_MINUS_6DB 0x0c | ||
794 | +#define HSL_GAIN 0x0c | ||
795 | +#define HSL_GAIN_PWR_DOWN 0x00 | ||
796 | +#define HSL_GAIN_PLUS_6DB 0x01 | ||
797 | +#define HSL_GAIN_0DB 0x02 | ||
798 | +#define HSL_GAIN_MINUS_6DB 0x03 | ||
799 | + | ||
800 | +/* HS_POPN_SET (0x24) Fields */ | ||
801 | + | ||
802 | +#define VMID_EN 0x40 | ||
803 | +#define EXTMUTE 0x20 | ||
804 | +#define RAMP_DELAY 0x1C | ||
805 | +#define RAMP_DELAY_20MS 0x00 | ||
806 | +#define RAMP_DELAY_40MS 0x04 | ||
807 | +#define RAMP_DELAY_81MS 0x08 | ||
808 | +#define RAMP_DELAY_161MS 0x0c | ||
809 | +#define RAMP_DELAY_323MS 0x10 | ||
810 | +#define RAMP_DELAY_645MS 0x14 | ||
811 | +#define RAMP_DELAY_1291MS 0x18 | ||
812 | +#define RAMP_DELAY_2581MS 0x1c | ||
813 | +#define RAMP_EN 0x02 | ||
814 | + | ||
815 | +extern struct snd_soc_codec_dai twl4030_dai; | ||
816 | +extern struct snd_soc_codec_device soc_codec_dev_twl4030; | ||
817 | + | ||
818 | +#endif /* End of __TWL4030_AUDIO_H__ */ | ||
819 | diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig | ||
820 | index 0230d83..8703cea 100644 | ||
821 | --- a/sound/soc/omap/Kconfig | ||
822 | +++ b/sound/soc/omap/Kconfig | ||
823 | @@ -16,4 +16,20 @@ config SND_OMAP_SOC_N810 | ||
824 | help | ||
825 | Say Y if you want to add support for SoC audio on Nokia N810. | ||
826 | |||
827 | +config SND_OMAP_SOC_OMAP3EVM | ||
828 | + tristate "SoC Audio support for OMAP3 EVM" | ||
829 | + depends on SND_OMAP_SOC && MACH_OMAP3EVM | ||
830 | + select SND_OMAP_SOC_MCBSP | ||
831 | + select SND_SOC_TWL4030 | ||
832 | + help | ||
833 | + Say Y if you want to add support for SoC audio on the OMAP3 EVM. | ||
834 | + | ||
835 | +config SND_OMAP_SOC_OMAP3BEAGLE | ||
836 | + tristate "SoC Audio support for OMAP3 Beagle" | ||
837 | + depends on SND_OMAP_SOC && MACH_OMAP3_BEAGLE | ||
838 | + select SND_OMAP_SOC_MCBSP | ||
839 | + select SND_SOC_TWL4030 | ||
840 | + help | ||
841 | + Say Y if you want to add support for SoC audio on the OMAP3 Beagle. | ||
842 | + | ||
843 | endmenu | ||
844 | diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile | ||
845 | index d8d8d58..638a240 100644 | ||
846 | --- a/sound/soc/omap/Makefile | ||
847 | +++ b/sound/soc/omap/Makefile | ||
848 | @@ -7,5 +7,10 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | ||
849 | |||
850 | # OMAP Machine Support | ||
851 | snd-soc-n810-objs := n810.o | ||
852 | +snd-soc-omap3evm-objs := omap3evm.o | ||
853 | +snd-soc-omap3beagle-objs := omap3beagle.o | ||
854 | |||
855 | obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o | ||
856 | +obj-$(CONFIG_SND_OMAP_SOC_OMAP3EVM) += snd-soc-omap3evm.o | ||
857 | +obj-$(CONFIG_SND_OMAP_SOC_OMAP3BEAGLE) += snd-soc-omap3beagle.o | ||
858 | + | ||
859 | diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c | ||
860 | new file mode 100644 | ||
861 | index 0000000..878f894 | ||
862 | --- /dev/null | ||
863 | +++ b/sound/soc/omap/omap3beagle.c | ||
864 | @@ -0,0 +1,142 @@ | ||
865 | +/* | ||
866 | + * omap3beagle.c -- SoC audio for OMAP3 Beagle | ||
867 | + * | ||
868 | + * Author: Steve Sakoman <steve@sakoman.com> | ||
869 | + * | ||
870 | + * This program is free software; you can redistribute it and/or | ||
871 | + * modify it under the terms of the GNU General Public License | ||
872 | + * version 2 as published by the Free Software Foundation. | ||
873 | + * | ||
874 | + * This program is distributed in the hope that it will be useful, but | ||
875 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
876 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
877 | + * General Public License for more details. | ||
878 | + * | ||
879 | + * You should have received a copy of the GNU General Public License | ||
880 | + * along with this program; if not, write to the Free Software | ||
881 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
882 | + * 02110-1301 USA | ||
883 | + * | ||
884 | + */ | ||
885 | + | ||
886 | +#include <linux/clk.h> | ||
887 | +#include <linux/platform_device.h> | ||
888 | +#include <sound/core.h> | ||
889 | +#include <sound/pcm.h> | ||
890 | +#include <sound/soc.h> | ||
891 | +#include <sound/soc-dapm.h> | ||
892 | + | ||
893 | +#include <asm/mach-types.h> | ||
894 | +#include <asm/arch/hardware.h> | ||
895 | +#include <asm/arch/gpio.h> | ||
896 | +#include <asm/arch/mcbsp.h> | ||
897 | + | ||
898 | +#include "omap-mcbsp.h" | ||
899 | +#include "omap-pcm.h" | ||
900 | +#include "../codecs/twl4030.h" | ||
901 | + | ||
902 | +static int omap3beagle_hw_params(struct snd_pcm_substream *substream, | ||
903 | + struct snd_pcm_hw_params *params) | ||
904 | +{ | ||
905 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
906 | + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
907 | + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
908 | + int ret; | ||
909 | + | ||
910 | + /* Set codec DAI configuration */ | ||
911 | + ret = codec_dai->dai_ops.set_fmt(codec_dai, | ||
912 | + SND_SOC_DAIFMT_I2S | | ||
913 | + SND_SOC_DAIFMT_NB_NF | | ||
914 | + SND_SOC_DAIFMT_CBM_CFM); | ||
915 | + if (ret < 0) { | ||
916 | + printk(KERN_INFO "can't set codec DAI configuration\n"); | ||
917 | + return ret; | ||
918 | + } | ||
919 | + | ||
920 | + /* Set cpu DAI configuration */ | ||
921 | + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, | ||
922 | + SND_SOC_DAIFMT_I2S | | ||
923 | + SND_SOC_DAIFMT_NB_NF | | ||
924 | + SND_SOC_DAIFMT_CBM_CFM); | ||
925 | + if (ret < 0) { | ||
926 | + printk(KERN_INFO "can't set cpu DAI configuration\n"); | ||
927 | + return ret; | ||
928 | + } | ||
929 | + | ||
930 | + return 0; | ||
931 | +} | ||
932 | + | ||
933 | +static struct snd_soc_ops omap3beagle_ops = { | ||
934 | + .hw_params = omap3beagle_hw_params, | ||
935 | +}; | ||
936 | + | ||
937 | +/* Digital audio interface glue - connects codec <--> CPU */ | ||
938 | +static struct snd_soc_dai_link omap3beagle_dai = { | ||
939 | + .name = "TWL4030", | ||
940 | + .stream_name = "TWL4030", | ||
941 | + .cpu_dai = &omap_mcbsp_dai[0], | ||
942 | + .codec_dai = &twl4030_dai, | ||
943 | + .ops = &omap3beagle_ops, | ||
944 | +}; | ||
945 | + | ||
946 | +/* Audio machine driver */ | ||
947 | +static struct snd_soc_machine snd_soc_machine_omap3beagle = { | ||
948 | + .name = "omap3beagle", | ||
949 | + .dai_link = &omap3beagle_dai, | ||
950 | + .num_links = 1, | ||
951 | +}; | ||
952 | + | ||
953 | +/* Audio subsystem */ | ||
954 | +static struct snd_soc_device omap3beagle_snd_devdata = { | ||
955 | + .machine = &snd_soc_machine_omap3beagle, | ||
956 | + .platform = &omap_soc_platform, | ||
957 | + .codec_dev = &soc_codec_dev_twl4030, | ||
958 | +}; | ||
959 | + | ||
960 | +static struct platform_device *omap3beagle_snd_device; | ||
961 | + | ||
962 | +static int __init omap3beagle_soc_init(void) | ||
963 | +{ | ||
964 | + int ret; | ||
965 | + | ||
966 | + printk(KERN_INFO "OMAP3 Beagle SoC init\n"); | ||
967 | + if (!machine_is_omap3_beagle()) { | ||
968 | + printk(KERN_INFO "Not OMAP3 Beagle!\n"); | ||
969 | + return -ENODEV; | ||
970 | + } | ||
971 | + | ||
972 | + omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); | ||
973 | + if (!omap3beagle_snd_device) { | ||
974 | + printk(KERN_INFO "Platform device allocation failed\n"); | ||
975 | + return -ENOMEM; | ||
976 | + } | ||
977 | + | ||
978 | + platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata); | ||
979 | + omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev; | ||
980 | + *(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */ | ||
981 | + | ||
982 | + ret = platform_device_add(omap3beagle_snd_device); | ||
983 | + if (ret) | ||
984 | + goto err1; | ||
985 | + | ||
986 | + return 0; | ||
987 | + | ||
988 | +err1: | ||
989 | + printk(KERN_INFO "Unable to add platform device\n"); | ||
990 | + platform_device_put(omap3beagle_snd_device); | ||
991 | + | ||
992 | + return ret; | ||
993 | +} | ||
994 | + | ||
995 | +static void __exit omap3beagle_soc_exit(void) | ||
996 | +{ | ||
997 | + printk(KERN_INFO "OMAP3 Beagle SoC exit\n"); | ||
998 | + platform_device_unregister(omap3beagle_snd_device); | ||
999 | +} | ||
1000 | + | ||
1001 | +module_init(omap3beagle_soc_init); | ||
1002 | +module_exit(omap3beagle_soc_exit); | ||
1003 | + | ||
1004 | +MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>"); | ||
1005 | +MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle"); | ||
1006 | +MODULE_LICENSE("GPL"); | ||
1007 | diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c | ||
1008 | new file mode 100644 | ||
1009 | index 0000000..a64c788 | ||
1010 | --- /dev/null | ||
1011 | +++ b/sound/soc/omap/omap3evm.c | ||
1012 | @@ -0,0 +1,142 @@ | ||
1013 | +/* | ||
1014 | + * omap3evm.c -- SoC audio for OMAP3 EVM | ||
1015 | + * | ||
1016 | + * Author: Steve Sakoman <steve@sakoman.com> | ||
1017 | + * | ||
1018 | + * This program is free software; you can redistribute it and/or | ||
1019 | + * modify it under the terms of the GNU General Public License | ||
1020 | + * version 2 as published by the Free Software Foundation. | ||
1021 | + * | ||
1022 | + * This program is distributed in the hope that it will be useful, but | ||
1023 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1024 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
1025 | + * General Public License for more details. | ||
1026 | + * | ||
1027 | + * You should have received a copy of the GNU General Public License | ||
1028 | + * along with this program; if not, write to the Free Software | ||
1029 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
1030 | + * 02110-1301 USA | ||
1031 | + * | ||
1032 | + */ | ||
1033 | + | ||
1034 | +#include <linux/clk.h> | ||
1035 | +#include <linux/platform_device.h> | ||
1036 | +#include <sound/core.h> | ||
1037 | +#include <sound/pcm.h> | ||
1038 | +#include <sound/soc.h> | ||
1039 | +#include <sound/soc-dapm.h> | ||
1040 | + | ||
1041 | +#include <asm/mach-types.h> | ||
1042 | +#include <asm/arch/hardware.h> | ||
1043 | +#include <asm/arch/gpio.h> | ||
1044 | +#include <asm/arch/mcbsp.h> | ||
1045 | + | ||
1046 | +#include "omap-mcbsp.h" | ||
1047 | +#include "omap-pcm.h" | ||
1048 | +#include "../codecs/twl4030.h" | ||
1049 | + | ||
1050 | +static int omap3evm_hw_params(struct snd_pcm_substream *substream, | ||
1051 | + struct snd_pcm_hw_params *params) | ||
1052 | +{ | ||
1053 | + struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
1054 | + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
1055 | + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
1056 | + int ret; | ||
1057 | + | ||
1058 | + /* Set codec DAI configuration */ | ||
1059 | + ret = codec_dai->dai_ops.set_fmt(codec_dai, | ||
1060 | + SND_SOC_DAIFMT_I2S | | ||
1061 | + SND_SOC_DAIFMT_NB_NF | | ||
1062 | + SND_SOC_DAIFMT_CBM_CFM); | ||
1063 | + if (ret < 0) { | ||
1064 | + printk(KERN_INFO "can't set codec DAI configuration\n"); | ||
1065 | + return ret; | ||
1066 | + } | ||
1067 | + | ||
1068 | + /* Set cpu DAI configuration */ | ||
1069 | + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, | ||
1070 | + SND_SOC_DAIFMT_I2S | | ||
1071 | + SND_SOC_DAIFMT_NB_NF | | ||
1072 | + SND_SOC_DAIFMT_CBM_CFM); | ||
1073 | + if (ret < 0) { | ||
1074 | + printk(KERN_INFO "can't set cpu DAI configuration\n"); | ||
1075 | + return ret; | ||
1076 | + } | ||
1077 | + | ||
1078 | + return 0; | ||
1079 | +} | ||
1080 | + | ||
1081 | +static struct snd_soc_ops omap3evm_ops = { | ||
1082 | + .hw_params = omap3evm_hw_params, | ||
1083 | +}; | ||
1084 | + | ||
1085 | +/* Digital audio interface glue - connects codec <--> CPU */ | ||
1086 | +static struct snd_soc_dai_link omap3evm_dai = { | ||
1087 | + .name = "TWL4030", | ||
1088 | + .stream_name = "TWL4030", | ||
1089 | + .cpu_dai = &omap_mcbsp_dai[0], | ||
1090 | + .codec_dai = &twl4030_dai, | ||
1091 | + .ops = &omap3evm_ops, | ||
1092 | +}; | ||
1093 | + | ||
1094 | +/* Audio machine driver */ | ||
1095 | +static struct snd_soc_machine snd_soc_machine_omap3evm = { | ||
1096 | + .name = "omap3evm", | ||
1097 | + .dai_link = &omap3evm_dai, | ||
1098 | + .num_links = 1, | ||
1099 | +}; | ||
1100 | + | ||
1101 | +/* Audio subsystem */ | ||
1102 | +static struct snd_soc_device omap3evm_snd_devdata = { | ||
1103 | + .machine = &snd_soc_machine_omap3evm, | ||
1104 | + .platform = &omap_soc_platform, | ||
1105 | + .codec_dev = &soc_codec_dev_twl4030, | ||
1106 | +}; | ||
1107 | + | ||
1108 | +static struct platform_device *omap3evm_snd_device; | ||
1109 | + | ||
1110 | +static int __init omap3evm_soc_init(void) | ||
1111 | +{ | ||
1112 | + int ret; | ||
1113 | + | ||
1114 | + printk(KERN_INFO "OMAP3 EVM SoC init\n"); | ||
1115 | + if (!machine_is_omap3evm()) { | ||
1116 | + printk(KERN_INFO "Not OMAP3 EVM!\n"); | ||
1117 | + return -ENODEV; | ||
1118 | + } | ||
1119 | + | ||
1120 | + omap3evm_snd_device = platform_device_alloc("soc-audio", -1); | ||
1121 | + if (!omap3evm_snd_device) { | ||
1122 | + printk(KERN_INFO "Platform device allocation failed\n"); | ||
1123 | + return -ENOMEM; | ||
1124 | + } | ||
1125 | + | ||
1126 | + platform_set_drvdata(omap3evm_snd_device, &omap3evm_snd_devdata); | ||
1127 | + omap3evm_snd_devdata.dev = &omap3evm_snd_device->dev; | ||
1128 | + *(unsigned int *)omap3evm_dai.cpu_dai->private_data = 1; /* McBSP2 */ | ||
1129 | + | ||
1130 | + ret = platform_device_add(omap3evm_snd_device); | ||
1131 | + if (ret) | ||
1132 | + goto err1; | ||
1133 | + | ||
1134 | + return 0; | ||
1135 | + | ||
1136 | +err1: | ||
1137 | + printk(KERN_INFO "Unable to add platform device\n"); | ||
1138 | + platform_device_put(omap3evm_snd_device); | ||
1139 | + | ||
1140 | + return ret; | ||
1141 | +} | ||
1142 | + | ||
1143 | +static void __exit omap3evm_soc_exit(void) | ||
1144 | +{ | ||
1145 | + printk(KERN_INFO "OMAP3 EVM SoC exit\n"); | ||
1146 | + platform_device_unregister(omap3evm_snd_device); | ||
1147 | +} | ||
1148 | + | ||
1149 | +module_init(omap3evm_soc_init); | ||
1150 | +module_exit(omap3evm_soc_exit); | ||
1151 | + | ||
1152 | +MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>"); | ||
1153 | +MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM"); | ||
1154 | +MODULE_LICENSE("GPL"); | ||