diff options
author | Liam Girdwood <lrg@ti.com> | 2011-07-24 15:59:47 -0400 |
---|---|---|
committer | Paolo Pisati <paolo.pisati@canonical.com> | 2012-08-17 04:18:27 -0400 |
commit | cd575d24e2d09d81ee02685f7c3116626e00319c (patch) | |
tree | f683d01f058d6d067e4a2a4a0b82cdb9fb6bddee | |
parent | 0532a15e8acd2dcd60fbc6473500b2e970df16e1 (diff) |
Subject: [PATCH 083/104] ASoC: OMAP4 - DMIC DAI driver
Add both legacy DMA and ABE support for Digital Microphones on the
OMAP4 platform.
Signed-off-by: Liam Girdwood <lrg@ti.com>
-rw-r--r-- | sound/soc/omap/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/omap-dmic.c | 623 | ||||
-rw-r--r-- | sound/soc/omap/omap-dmic.h | 23 |
4 files changed, 651 insertions, 0 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index fb5d2a87a55..e28ca02c71a 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -15,6 +15,9 @@ config SND_OMAP_SOC_MCPDM | |||
15 | config SND_OMAP_SOC_ABE | 15 | config SND_OMAP_SOC_ABE |
16 | tristate | 16 | tristate |
17 | 17 | ||
18 | config SND_OMAP_SOC_DMIC | ||
19 | tristate | ||
20 | |||
18 | config SND_OMAP_SOC_N810 | 21 | config SND_OMAP_SOC_N810 |
19 | tristate "SoC Audio support for Nokia N810" | 22 | tristate "SoC Audio support for Nokia N810" |
20 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C | 23 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C |
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 0d90f649ce7..7cc549ddb44 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -2,12 +2,14 @@ | |||
2 | snd-soc-omap-objs := omap-pcm.o | 2 | snd-soc-omap-objs := omap-pcm.o |
3 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o | 3 | snd-soc-omap-mcbsp-objs := omap-mcbsp.o |
4 | snd-soc-omap-mcpdm-objs := omap-mcpdm.o | 4 | snd-soc-omap-mcpdm-objs := omap-mcpdm.o |
5 | snd-soc-omap-dmic-objs := omap-dmic.o | ||
5 | snd-soc-omap-abe-objs := omap-abe.o | 6 | snd-soc-omap-abe-objs := omap-abe.o |
6 | snd-soc-omap-abe-dsp-objs := omap-abe-dsp.o | 7 | snd-soc-omap-abe-dsp-objs := omap-abe-dsp.o |
7 | 8 | ||
8 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o | 9 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o |
9 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | 10 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o |
10 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o | 11 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o |
12 | obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o | ||
11 | obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o | 13 | obj-$(CONFIG_SND_OMAP_SOC_ABE) += snd-soc-omap-abe.o |
12 | obj-$(CONFIG_SND_OMAP_SOC_ABE_DSP) += snd-soc-omap-abe-dsp.o abe/ | 14 | obj-$(CONFIG_SND_OMAP_SOC_ABE_DSP) += snd-soc-omap-abe-dsp.o abe/ |
13 | 15 | ||
diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c new file mode 100644 index 00000000000..5f8aa2d1a35 --- /dev/null +++ b/sound/soc/omap/omap-dmic.c | |||
@@ -0,0 +1,623 @@ | |||
1 | /* | ||
2 | * omap-dmic.c -- OMAP ASoC DMIC DAI driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Texas Instruments | ||
5 | * | ||
6 | * Author: Liam Girdwood <lrg@ti.com> | ||
7 | * David Lambert <dlambert@ti.com> | ||
8 | * Misael Lopez Cruz <misael.lopez@ti.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * version 2 as published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
22 | * 02110-1301 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #undef DEBUG | ||
27 | |||
28 | #include <linux/init.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/err.h> | ||
32 | #include <linux/clk.h> | ||
33 | #include <linux/io.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/pm_runtime.h> | ||
36 | |||
37 | #include <plat/dma.h> | ||
38 | #include <plat/dmic.h> | ||
39 | |||
40 | #include <sound/core.h> | ||
41 | #include <sound/pcm.h> | ||
42 | #include <sound/pcm_params.h> | ||
43 | #include <sound/initval.h> | ||
44 | #include <sound/soc.h> | ||
45 | |||
46 | #include "omap-pcm.h" | ||
47 | #include "omap-dmic.h" | ||
48 | |||
49 | #define OMAP_DMIC_RATES (SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) | ||
50 | #define OMAP_DMIC_FORMATS SNDRV_PCM_FMTBIT_S32_LE | ||
51 | |||
52 | #define OMAP4_LEGACY_DMIC0 0 | ||
53 | #define OMAP4_ABE_DMIC0 1 | ||
54 | #define OMAP4_ABE_DMIC1 2 | ||
55 | #define OMAP4_ABE_DMIC2 3 | ||
56 | |||
57 | struct omap_dmic { | ||
58 | struct device *dev; | ||
59 | void __iomem *io_base; | ||
60 | int clk_freq; | ||
61 | int sysclk; | ||
62 | int active; | ||
63 | int running; | ||
64 | int channels; | ||
65 | int abe_mode; | ||
66 | u32 up_enable; | ||
67 | struct mutex mutex; | ||
68 | }; | ||
69 | |||
70 | /* | ||
71 | * Stream DMA parameters | ||
72 | */ | ||
73 | static struct omap_pcm_dma_data omap_dmic_dai_dma_params = { | ||
74 | .name = "DMIC capture", | ||
75 | .data_type = OMAP_DMA_DATA_TYPE_S32, | ||
76 | .sync_mode = OMAP_DMA_SYNC_PACKET, | ||
77 | .packet_size = 2, | ||
78 | .port_addr = OMAP44XX_DMIC_L3_BASE + OMAP_DMIC_DATA, | ||
79 | }; | ||
80 | |||
81 | static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) | ||
82 | { | ||
83 | __raw_writel(val, dmic->io_base + reg); | ||
84 | } | ||
85 | |||
86 | static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg) | ||
87 | { | ||
88 | return __raw_readl(dmic->io_base + reg); | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * Enables and disables DMIC channels through the DMIC interface | ||
93 | */ | ||
94 | static inline void dmic_set_up_channels(struct omap_dmic *dmic) | ||
95 | { | ||
96 | u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL) & ~OMAP_DMIC_UP_ENABLE_MASK; | ||
97 | omap_dmic_write(dmic, OMAP_DMIC_CTRL, ctrl | dmic->up_enable); | ||
98 | } | ||
99 | |||
100 | static inline int dmic_is_enabled(struct omap_dmic *dmic) | ||
101 | { | ||
102 | return omap_dmic_read(dmic, OMAP_DMIC_CTRL) & OMAP_DMIC_UP_ENABLE_MASK; | ||
103 | } | ||
104 | |||
105 | static int omap_dmic_set_clkdiv(struct snd_soc_dai *dai, | ||
106 | int div_id, int div) | ||
107 | { | ||
108 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
109 | int div_sel = -EINVAL; | ||
110 | u32 ctrl; | ||
111 | |||
112 | if (div_id != OMAP_DMIC_CLKDIV) | ||
113 | return -ENODEV; | ||
114 | |||
115 | switch (dmic->clk_freq) { | ||
116 | case 19200000: | ||
117 | switch (div) { | ||
118 | case 5: | ||
119 | div_sel = 0x1; | ||
120 | break; | ||
121 | case 8: | ||
122 | div_sel = 0x0; | ||
123 | break; | ||
124 | default: | ||
125 | dev_err(dai->dev, "invalid div_sel (%d) for 19200000Hz", div); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | break; | ||
129 | case 24000000: | ||
130 | switch (div) { | ||
131 | case 10: | ||
132 | div_sel = 0x2; | ||
133 | break; | ||
134 | default: | ||
135 | dev_err(dai->dev, "invalid div_sel (%d) for 24000000Hz", div); | ||
136 | return -EINVAL; | ||
137 | } | ||
138 | break; | ||
139 | case 24576000: | ||
140 | switch (div) { | ||
141 | case 8: | ||
142 | div_sel = 0x3; | ||
143 | break; | ||
144 | case 16: | ||
145 | div_sel = 0x4; | ||
146 | break; | ||
147 | default: | ||
148 | dev_err(dai->dev, "invalid div_sel (%d) for 24576000Hz", div); | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | break; | ||
152 | case 12000000: | ||
153 | switch (div) { | ||
154 | case 5: | ||
155 | div_sel = 0x5; | ||
156 | break; | ||
157 | default: | ||
158 | dev_err(dai->dev, "invalid div_sel (%d) for 12000000Hz", div); | ||
159 | return -EINVAL; | ||
160 | } | ||
161 | break; | ||
162 | default: | ||
163 | dev_err(dai->dev, "invalid freq %d\n", dmic->clk_freq); | ||
164 | return -EINVAL; | ||
165 | } | ||
166 | |||
167 | if (div_sel < 0) { | ||
168 | dev_err(dai->dev, "divider not supported %d\n", div); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL) & ~OMAP_DMIC_CLK_DIV_MASK; | ||
173 | |||
174 | omap_dmic_write(dmic, OMAP_DMIC_CTRL, | ||
175 | ctrl | (div_sel << OMAP_DMIC_CLK_DIV_SHIFT)); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Configures DMIC for audio recording. | ||
182 | * This function should be called before omap_dmic_start. | ||
183 | */ | ||
184 | static void omap_dmic_open(struct omap_dmic *dmic) | ||
185 | { | ||
186 | u32 ctrl; | ||
187 | |||
188 | /* Configure uplink threshold */ | ||
189 | omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL, 2); | ||
190 | |||
191 | /* Set dmic out format */ | ||
192 | ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL) | ||
193 | & ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK); | ||
194 | omap_dmic_write(dmic, OMAP_DMIC_CTRL, | ||
195 | ctrl | OMAP_DMICOUTFORMAT_LJUST | | ||
196 | OMAP_DMIC_POLAR1 | OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3); | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Cleans DMIC uplink configuration. | ||
201 | * This function should be called when the stream is closed. | ||
202 | */ | ||
203 | static void omap_dmic_close(struct omap_dmic *dmic) | ||
204 | { | ||
205 | /* Disable DMA request generation */ | ||
206 | omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR, OMAP_DMIC_DMA_ENABLE); | ||
207 | |||
208 | } | ||
209 | |||
210 | static int omap_dmic_dai_startup(struct snd_pcm_substream *substream, | ||
211 | struct snd_soc_dai *dai) | ||
212 | { | ||
213 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
214 | int ret = 0; | ||
215 | |||
216 | mutex_lock(&dmic->mutex); | ||
217 | |||
218 | if (!dmic->active) { | ||
219 | pm_runtime_get_sync(dmic->dev); | ||
220 | |||
221 | if (dai->id > OMAP4_LEGACY_DMIC0) | ||
222 | dmic->abe_mode = 1; | ||
223 | |||
224 | omap_dmic_open(dmic); | ||
225 | } else { | ||
226 | /* legacy and ABE mode are mutually exclusive */ | ||
227 | if (dai->id > OMAP4_LEGACY_DMIC0 && !dmic->abe_mode) { | ||
228 | ret = -EBUSY; | ||
229 | goto out; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | dmic->active++; | ||
234 | |||
235 | out: | ||
236 | mutex_unlock(&dmic->mutex); | ||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream, | ||
241 | struct snd_soc_dai *dai) | ||
242 | { | ||
243 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
244 | |||
245 | mutex_lock(&dmic->mutex); | ||
246 | |||
247 | if (--dmic->active == 0) { | ||
248 | omap_dmic_close(dmic); | ||
249 | pm_runtime_put_sync(dmic->dev); | ||
250 | dmic->abe_mode = 0; | ||
251 | } | ||
252 | |||
253 | mutex_unlock(&dmic->mutex); | ||
254 | } | ||
255 | |||
256 | static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, | ||
257 | struct snd_pcm_hw_params *params, | ||
258 | struct snd_soc_dai *dai) | ||
259 | { | ||
260 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
261 | int channels, rate, div; | ||
262 | int ret = 0; | ||
263 | |||
264 | channels = params_channels(params); | ||
265 | if (dai->id == OMAP4_LEGACY_DMIC0) { | ||
266 | switch (channels) { | ||
267 | case 2: | ||
268 | case 4: | ||
269 | case 6: | ||
270 | dmic->channels = channels; | ||
271 | break; | ||
272 | default: | ||
273 | dev_err(dmic->dev, "invalid number of legacy channels\n"); | ||
274 | return -EINVAL; | ||
275 | } | ||
276 | } else { | ||
277 | if (channels != 2) { | ||
278 | dev_err(dmic->dev, "invalid number of ABE channels\n"); | ||
279 | return -EINVAL; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | rate = params_rate(params); | ||
284 | switch (rate) { | ||
285 | case 96000: | ||
286 | div = 8; | ||
287 | break; | ||
288 | case 192000: | ||
289 | div = 5; | ||
290 | break; | ||
291 | default: | ||
292 | dev_err(dmic->dev, "rate %d not supported\n", rate); | ||
293 | return -EINVAL; | ||
294 | } | ||
295 | |||
296 | /* packet size is threshold * channels */ | ||
297 | omap_dmic_dai_dma_params.packet_size = 2 * channels; | ||
298 | snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params); | ||
299 | |||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static void dmic_config_up_channels(struct omap_dmic *dmic, int dai_id, | ||
304 | int enable) | ||
305 | { | ||
306 | if (enable) { | ||
307 | switch (dai_id) { | ||
308 | case OMAP4_LEGACY_DMIC0: | ||
309 | switch (dmic->channels) { | ||
310 | case 6: | ||
311 | dmic->up_enable = OMAP_DMIC_UP1_ENABLE | OMAP_DMIC_UP2_ENABLE | ||
312 | | OMAP_DMIC_UP3_ENABLE; | ||
313 | break; | ||
314 | case 4: | ||
315 | dmic->up_enable = OMAP_DMIC_UP1_ENABLE | OMAP_DMIC_UP2_ENABLE; | ||
316 | break; | ||
317 | case 2: | ||
318 | dmic->up_enable = OMAP_DMIC_UP1_ENABLE; | ||
319 | break; | ||
320 | default: | ||
321 | break; | ||
322 | } | ||
323 | break; | ||
324 | case OMAP4_ABE_DMIC0: | ||
325 | case OMAP4_ABE_DMIC1: | ||
326 | case OMAP4_ABE_DMIC2: | ||
327 | /* | ||
328 | * ABE expects all the DMIC interfaces to be | ||
329 | * enabled, so enabling them when at least one | ||
330 | * DMIC DAI is running | ||
331 | */ | ||
332 | if (dmic->running) | ||
333 | dmic->up_enable |= OMAP_DMIC_UP1_ENABLE | | ||
334 | OMAP_DMIC_UP2_ENABLE | | ||
335 | OMAP_DMIC_UP3_ENABLE; | ||
336 | break; | ||
337 | default: | ||
338 | break; | ||
339 | } | ||
340 | } else { | ||
341 | switch (dai_id) { | ||
342 | case OMAP4_LEGACY_DMIC0: | ||
343 | dmic->up_enable = 0; | ||
344 | break; | ||
345 | case OMAP4_ABE_DMIC0: | ||
346 | case OMAP4_ABE_DMIC1: | ||
347 | case OMAP4_ABE_DMIC2: | ||
348 | /* | ||
349 | * Disable all DMIC interfaces only when | ||
350 | * all DAIs are stopped | ||
351 | */ | ||
352 | if (!dmic->running) | ||
353 | dmic->up_enable = 0; | ||
354 | break; | ||
355 | default: | ||
356 | break; | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream, | ||
362 | struct snd_soc_dai *dai) | ||
363 | { | ||
364 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
365 | |||
366 | /* Configure DMA controller */ | ||
367 | omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET, OMAP_DMIC_DMA_ENABLE); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream, | ||
373 | int cmd, struct snd_soc_dai *dai) | ||
374 | { | ||
375 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
376 | |||
377 | switch (cmd) { | ||
378 | case SNDRV_PCM_TRIGGER_START: | ||
379 | dmic->running++; | ||
380 | dmic_config_up_channels(dmic, dai->id, 1); | ||
381 | dmic_set_up_channels(dmic); | ||
382 | break; | ||
383 | case SNDRV_PCM_TRIGGER_STOP: | ||
384 | dmic->running--; | ||
385 | dmic_config_up_channels(dmic, dai->id, 0); | ||
386 | dmic_set_up_channels(dmic); | ||
387 | break; | ||
388 | default: | ||
389 | break; | ||
390 | } | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, | ||
396 | int clk_id, unsigned int freq, | ||
397 | int dir) | ||
398 | { | ||
399 | struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); | ||
400 | struct clk *dmic_clk, *parent_clk; | ||
401 | int ret = 0; | ||
402 | |||
403 | dmic_clk = clk_get(NULL, "dmic_fck"); | ||
404 | if (IS_ERR(dmic_clk)) { | ||
405 | dev_err(dmic->dev, "cant get dmic_fck\n"); | ||
406 | return -ENODEV; | ||
407 | } | ||
408 | |||
409 | switch (clk_id) { | ||
410 | case OMAP_DMIC_SYSCLK_PAD_CLKS: | ||
411 | parent_clk = clk_get(NULL, "pad_clks_ck"); | ||
412 | if (IS_ERR(parent_clk)) { | ||
413 | dev_err(dmic->dev, "cant get pad_clks_ck\n"); | ||
414 | ret = -ENODEV; | ||
415 | goto err_par; | ||
416 | } | ||
417 | break; | ||
418 | case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS: | ||
419 | parent_clk = clk_get(NULL, "slimbus_clk"); | ||
420 | if (IS_ERR(parent_clk)) { | ||
421 | dev_err(dmic->dev, "cant get slimbus_clk\n"); | ||
422 | ret = -ENODEV; | ||
423 | goto err_par; | ||
424 | } | ||
425 | break; | ||
426 | case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS: | ||
427 | parent_clk = clk_get(NULL, "dmic_sync_mux_ck"); | ||
428 | if (IS_ERR(parent_clk)) { | ||
429 | dev_err(dmic->dev, "cant get dmic_sync_mux_ck\n"); | ||
430 | ret = -ENODEV; | ||
431 | goto err_par; | ||
432 | } | ||
433 | break; | ||
434 | default: | ||
435 | dev_err(dai->dev, "clk_id not supported %d\n", clk_id); | ||
436 | ret = -EINVAL; | ||
437 | goto err_par; | ||
438 | } | ||
439 | |||
440 | if (dmic->sysclk != clk_id) { | ||
441 | /* re-parent not allowed if a stream is ongoing */ | ||
442 | if (dmic_is_enabled(dmic)) { | ||
443 | dev_err(dmic->dev, "cant re-parent when DMIC active\n"); | ||
444 | ret = -EBUSY; | ||
445 | goto err_busy; | ||
446 | } | ||
447 | |||
448 | /* disable clock while reparenting */ | ||
449 | pm_runtime_put_sync(dmic->dev); | ||
450 | ret = clk_set_parent(dmic_clk, parent_clk); | ||
451 | pm_runtime_get_sync(dmic->dev); | ||
452 | if (ret < 0) { | ||
453 | dev_err(dmic->dev, "re-parent failed\n"); | ||
454 | goto err_busy; | ||
455 | } | ||
456 | |||
457 | dmic->sysclk = clk_id; | ||
458 | |||
459 | //ret = clk_set_rate(dmic_clk, freq); | ||
460 | if (ret < 0) | ||
461 | dev_err(dmic->dev, "clock set to %d Hz failed\n", freq); | ||
462 | else | ||
463 | dmic->clk_freq = freq; | ||
464 | } | ||
465 | |||
466 | err_busy: | ||
467 | clk_put(parent_clk); | ||
468 | err_par: | ||
469 | clk_put(dmic_clk); | ||
470 | |||
471 | return ret; | ||
472 | } | ||
473 | |||
474 | static struct snd_soc_dai_ops omap_dmic_dai_ops = { | ||
475 | .startup = omap_dmic_dai_startup, | ||
476 | .shutdown = omap_dmic_dai_shutdown, | ||
477 | .hw_params = omap_dmic_dai_hw_params, | ||
478 | .prepare = omap_dmic_dai_prepare, | ||
479 | .trigger = omap_dmic_dai_trigger, | ||
480 | .set_sysclk = omap_dmic_set_dai_sysclk, | ||
481 | .set_clkdiv = omap_dmic_set_clkdiv, | ||
482 | }; | ||
483 | |||
484 | static struct snd_soc_dai_driver omap_dmic_dai[] = { | ||
485 | { | ||
486 | .name = "omap-dmic-dai-0", | ||
487 | .id = OMAP4_LEGACY_DMIC0, | ||
488 | .capture = { | ||
489 | .channels_min = 2, | ||
490 | .channels_max = 6, | ||
491 | .rates = OMAP_DMIC_RATES, | ||
492 | .formats = OMAP_DMIC_FORMATS, | ||
493 | }, | ||
494 | .ops = &omap_dmic_dai_ops, | ||
495 | }, | ||
496 | { | ||
497 | .name = "omap-dmic-abe-dai-0", | ||
498 | .id = OMAP4_ABE_DMIC0, | ||
499 | .capture = { | ||
500 | .channels_min = 2, | ||
501 | .channels_max = 2, | ||
502 | .rates = OMAP_DMIC_RATES, | ||
503 | .formats = OMAP_DMIC_FORMATS, | ||
504 | }, | ||
505 | .ops = &omap_dmic_dai_ops, | ||
506 | }, | ||
507 | { | ||
508 | .name = "omap-dmic-abe-dai-1", | ||
509 | .id = OMAP4_ABE_DMIC1, | ||
510 | .capture = { | ||
511 | .channels_min = 2, | ||
512 | .channels_max = 2, | ||
513 | .rates = OMAP_DMIC_RATES, | ||
514 | .formats = OMAP_DMIC_FORMATS, | ||
515 | }, | ||
516 | .ops = &omap_dmic_dai_ops, | ||
517 | }, | ||
518 | { | ||
519 | .name = "omap-dmic-abe-dai-2", | ||
520 | .id = OMAP4_ABE_DMIC2, | ||
521 | .capture = { | ||
522 | .channels_min = 2, | ||
523 | .channels_max = 2, | ||
524 | .rates = OMAP_DMIC_RATES, | ||
525 | .formats = OMAP_DMIC_FORMATS, | ||
526 | }, | ||
527 | .ops = &omap_dmic_dai_ops, | ||
528 | }, | ||
529 | }; | ||
530 | |||
531 | static __devinit int asoc_dmic_probe(struct platform_device *pdev) | ||
532 | { | ||
533 | struct omap_dmic *dmic; | ||
534 | struct resource *res; | ||
535 | int ret; | ||
536 | |||
537 | dmic = kzalloc(sizeof(struct omap_dmic), GFP_KERNEL); | ||
538 | if (!dmic) | ||
539 | return -ENOMEM; | ||
540 | |||
541 | platform_set_drvdata(pdev, dmic); | ||
542 | dmic->dev = &pdev->dev; | ||
543 | dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS; | ||
544 | |||
545 | mutex_init(&dmic->mutex); | ||
546 | |||
547 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
548 | if (!res) { | ||
549 | dev_err(dmic->dev, "invalid memory resource\n"); | ||
550 | ret = -ENODEV; | ||
551 | goto err_res; | ||
552 | } | ||
553 | |||
554 | dmic->io_base = ioremap(res->start, resource_size(res)); | ||
555 | if (!dmic->io_base) { | ||
556 | ret = -ENOMEM; | ||
557 | goto err_res; | ||
558 | } | ||
559 | |||
560 | res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
561 | if (!res) { | ||
562 | dev_err(dmic->dev, "invalid dma resource\n"); | ||
563 | ret = -ENODEV; | ||
564 | goto err_dai; | ||
565 | } | ||
566 | omap_dmic_dai_dma_params.dma_req = res->start; | ||
567 | |||
568 | pm_runtime_enable(dmic->dev); | ||
569 | |||
570 | /* Disable lines while request is ongoing */ | ||
571 | omap_dmic_write(dmic, OMAP_DMIC_CTRL, 0x00); | ||
572 | |||
573 | ret = snd_soc_register_dais(&pdev->dev, omap_dmic_dai, | ||
574 | ARRAY_SIZE(omap_dmic_dai)); | ||
575 | if (ret) | ||
576 | goto err_dai; | ||
577 | |||
578 | return 0; | ||
579 | |||
580 | err_dai: | ||
581 | iounmap(dmic->io_base); | ||
582 | err_res: | ||
583 | kfree(dmic); | ||
584 | return ret; | ||
585 | } | ||
586 | |||
587 | static int __devexit asoc_dmic_remove(struct platform_device *pdev) | ||
588 | { | ||
589 | struct omap_dmic *dmic = platform_get_drvdata(pdev); | ||
590 | |||
591 | snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(omap_dmic_dai)); | ||
592 | iounmap(dmic->io_base); | ||
593 | pm_runtime_disable(dmic->dev); | ||
594 | kfree(dmic); | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static struct platform_driver asoc_dmic_driver = { | ||
600 | .driver = { | ||
601 | .name = "omap-dmic-dai", | ||
602 | .owner = THIS_MODULE, | ||
603 | }, | ||
604 | .probe = asoc_dmic_probe, | ||
605 | .remove = __devexit_p(asoc_dmic_remove), | ||
606 | }; | ||
607 | |||
608 | static int __init snd_omap_dmic_init(void) | ||
609 | { | ||
610 | return platform_driver_register(&asoc_dmic_driver); | ||
611 | } | ||
612 | module_init(snd_omap_dmic_init); | ||
613 | |||
614 | static void __exit snd_omap_dmic_exit(void) | ||
615 | { | ||
616 | platform_driver_unregister(&asoc_dmic_driver); | ||
617 | } | ||
618 | module_exit(snd_omap_dmic_exit); | ||
619 | |||
620 | MODULE_ALIAS("platform:omap-dmic-dai"); | ||
621 | MODULE_AUTHOR("David Lambert <dlambert@ti.com>"); | ||
622 | MODULE_DESCRIPTION("OMAP DMIC ASoC Interface"); | ||
623 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/omap-dmic.h b/sound/soc/omap/omap-dmic.h new file mode 100644 index 00000000000..728fed2c043 --- /dev/null +++ b/sound/soc/omap/omap-dmic.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * omap-dmic.h -- OMAP Digital Microphone Controller | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _OMAP_DMIC_H | ||
10 | #define _OMAP_DMIC_H | ||
11 | |||
12 | enum omap_dmic_clk { | ||
13 | OMAP_DMIC_SYSCLK_PAD_CLKS, /* PAD_CLKS */ | ||
14 | OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS, /* SLIMBUS_CLK */ | ||
15 | OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS, /* DMIC_SYNC_MUX_CLK */ | ||
16 | }; | ||
17 | |||
18 | /* DMIC dividers */ | ||
19 | enum omap_dmic_div { | ||
20 | OMAP_DMIC_CLKDIV, | ||
21 | }; | ||
22 | |||
23 | #endif | ||