diff options
Diffstat (limited to 'sound/soc/omap')
-rw-r--r-- | sound/soc/omap/Kconfig | 11 | ||||
-rw-r--r-- | sound/soc/omap/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.c | 484 | ||||
-rw-r--r-- | sound/soc/omap/mcpdm.h | 151 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.c | 146 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcbsp.h | 4 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcpdm.c | 251 | ||||
-rw-r--r-- | sound/soc/omap/omap-mcpdm.h | 29 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.c | 15 | ||||
-rw-r--r-- | sound/soc/omap/omap-pcm.h | 4 | ||||
-rw-r--r-- | sound/soc/omap/omap3beagle.c | 6 | ||||
-rw-r--r-- | sound/soc/omap/omap3pandora.c | 42 |
12 files changed, 1122 insertions, 23 deletions
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 61952aa6cd5a..f11963c21873 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -6,6 +6,9 @@ config SND_OMAP_SOC_MCBSP | |||
6 | tristate | 6 | tristate |
7 | select OMAP_MCBSP | 7 | select OMAP_MCBSP |
8 | 8 | ||
9 | config SND_OMAP_SOC_MCPDM | ||
10 | tristate | ||
11 | |||
9 | config SND_OMAP_SOC_N810 | 12 | config SND_OMAP_SOC_N810 |
10 | tristate "SoC Audio support for Nokia N810" | 13 | tristate "SoC Audio support for Nokia N810" |
11 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C | 14 | depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C |
@@ -94,12 +97,14 @@ config SND_OMAP_SOC_OMAP3_PANDORA | |||
94 | Say Y if you want to add support for SoC audio on the OMAP3 Pandora. | 97 | Say Y if you want to add support for SoC audio on the OMAP3 Pandora. |
95 | 98 | ||
96 | config SND_OMAP_SOC_OMAP3_BEAGLE | 99 | config SND_OMAP_SOC_OMAP3_BEAGLE |
97 | tristate "SoC Audio support for OMAP3 Beagle" | 100 | tristate "SoC Audio support for OMAP3 Beagle and Devkit8000" |
98 | depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_BEAGLE | 101 | depends on TWL4030_CORE && SND_OMAP_SOC |
102 | depends on (MACH_OMAP3_BEAGLE || MACH_DEVKIT8000) | ||
99 | select SND_OMAP_SOC_MCBSP | 103 | select SND_OMAP_SOC_MCBSP |
100 | select SND_SOC_TWL4030 | 104 | select SND_SOC_TWL4030 |
101 | help | 105 | help |
102 | Say Y if you want to add support for SoC audio on the Beagleboard. | 106 | Say Y if you want to add support for SoC audio on the Beagleboard or |
107 | the clone Devkit8000. | ||
103 | 108 | ||
104 | config SND_OMAP_SOC_ZOOM2 | 109 | config SND_OMAP_SOC_ZOOM2 |
105 | tristate "SoC Audio support for Zoom2" | 110 | tristate "SoC Audio support for Zoom2" |
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 19283e5edfbf..0bc00ca14b37 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile | |||
@@ -1,9 +1,11 @@ | |||
1 | # OMAP Platform Support | 1 | # OMAP Platform Support |
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 mcpdm.o | ||
4 | 5 | ||
5 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o | 6 | obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o |
6 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o | 7 | obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o |
8 | obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o | ||
7 | 9 | ||
8 | # OMAP Machine Support | 10 | # OMAP Machine Support |
9 | snd-soc-n810-objs := n810.o | 11 | snd-soc-n810-objs := n810.o |
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c new file mode 100644 index 000000000000..ad8df6cfae88 --- /dev/null +++ b/sound/soc/omap/mcpdm.c | |||
@@ -0,0 +1,484 @@ | |||
1 | /* | ||
2 | * mcpdm.c -- McPDM interface driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * Copyright (C) 2009 - Texas Instruments, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <linux/irq.h> | ||
34 | |||
35 | #include "mcpdm.h" | ||
36 | |||
37 | static struct omap_mcpdm *mcpdm; | ||
38 | |||
39 | static inline void omap_mcpdm_write(u16 reg, u32 val) | ||
40 | { | ||
41 | __raw_writel(val, mcpdm->io_base + reg); | ||
42 | } | ||
43 | |||
44 | static inline int omap_mcpdm_read(u16 reg) | ||
45 | { | ||
46 | return __raw_readl(mcpdm->io_base + reg); | ||
47 | } | ||
48 | |||
49 | static void omap_mcpdm_reg_dump(void) | ||
50 | { | ||
51 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
52 | dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", | ||
53 | omap_mcpdm_read(MCPDM_IRQSTATUS_RAW)); | ||
54 | dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", | ||
55 | omap_mcpdm_read(MCPDM_IRQSTATUS)); | ||
56 | dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", | ||
57 | omap_mcpdm_read(MCPDM_IRQENABLE_SET)); | ||
58 | dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", | ||
59 | omap_mcpdm_read(MCPDM_IRQENABLE_CLR)); | ||
60 | dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", | ||
61 | omap_mcpdm_read(MCPDM_IRQWAKE_EN)); | ||
62 | dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", | ||
63 | omap_mcpdm_read(MCPDM_DMAENABLE_SET)); | ||
64 | dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", | ||
65 | omap_mcpdm_read(MCPDM_DMAENABLE_CLR)); | ||
66 | dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", | ||
67 | omap_mcpdm_read(MCPDM_DMAWAKEEN)); | ||
68 | dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", | ||
69 | omap_mcpdm_read(MCPDM_CTRL)); | ||
70 | dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", | ||
71 | omap_mcpdm_read(MCPDM_DN_DATA)); | ||
72 | dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", | ||
73 | omap_mcpdm_read(MCPDM_UP_DATA)); | ||
74 | dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", | ||
75 | omap_mcpdm_read(MCPDM_FIFO_CTRL_DN)); | ||
76 | dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", | ||
77 | omap_mcpdm_read(MCPDM_FIFO_CTRL_UP)); | ||
78 | dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n", | ||
79 | omap_mcpdm_read(MCPDM_DN_OFFSET)); | ||
80 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Takes the McPDM module in and out of reset state. | ||
85 | * Uplink and downlink can be reset individually. | ||
86 | */ | ||
87 | static void omap_mcpdm_reset_capture(int reset) | ||
88 | { | ||
89 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
90 | |||
91 | if (reset) | ||
92 | ctrl |= SW_UP_RST; | ||
93 | else | ||
94 | ctrl &= ~SW_UP_RST; | ||
95 | |||
96 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
97 | } | ||
98 | |||
99 | static void omap_mcpdm_reset_playback(int reset) | ||
100 | { | ||
101 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
102 | |||
103 | if (reset) | ||
104 | ctrl |= SW_DN_RST; | ||
105 | else | ||
106 | ctrl &= ~SW_DN_RST; | ||
107 | |||
108 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * Enables the transfer through the PDM interface to/from the Phoenix | ||
113 | * codec by enabling the corresponding UP or DN channels. | ||
114 | */ | ||
115 | void omap_mcpdm_start(int stream) | ||
116 | { | ||
117 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
118 | |||
119 | if (stream) | ||
120 | ctrl |= mcpdm->up_channels; | ||
121 | else | ||
122 | ctrl |= mcpdm->dn_channels; | ||
123 | |||
124 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Disables the transfer through the PDM interface to/from the Phoenix | ||
129 | * codec by disabling the corresponding UP or DN channels. | ||
130 | */ | ||
131 | void omap_mcpdm_stop(int stream) | ||
132 | { | ||
133 | int ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
134 | |||
135 | if (stream) | ||
136 | ctrl &= ~mcpdm->up_channels; | ||
137 | else | ||
138 | ctrl &= ~mcpdm->dn_channels; | ||
139 | |||
140 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Configures McPDM uplink for audio recording. | ||
145 | * This function should be called before omap_mcpdm_start. | ||
146 | */ | ||
147 | int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink) | ||
148 | { | ||
149 | int irq_mask = 0; | ||
150 | int ctrl; | ||
151 | |||
152 | if (!uplink) | ||
153 | return -EINVAL; | ||
154 | |||
155 | mcpdm->uplink = uplink; | ||
156 | |||
157 | /* Enable irq request generation */ | ||
158 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
159 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
160 | |||
161 | /* Configure uplink threshold */ | ||
162 | if (uplink->threshold > UP_THRES_MAX) | ||
163 | uplink->threshold = UP_THRES_MAX; | ||
164 | |||
165 | omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold); | ||
166 | |||
167 | /* Configure DMA controller */ | ||
168 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE); | ||
169 | |||
170 | /* Set pdm out format */ | ||
171 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
172 | ctrl &= ~PDMOUTFORMAT; | ||
173 | ctrl |= uplink->format & PDMOUTFORMAT; | ||
174 | |||
175 | /* Uplink channels */ | ||
176 | mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK); | ||
177 | |||
178 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Configures McPDM downlink for audio playback. | ||
185 | * This function should be called before omap_mcpdm_start. | ||
186 | */ | ||
187 | int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink) | ||
188 | { | ||
189 | int irq_mask = 0; | ||
190 | int ctrl; | ||
191 | |||
192 | if (!downlink) | ||
193 | return -EINVAL; | ||
194 | |||
195 | mcpdm->downlink = downlink; | ||
196 | |||
197 | /* Enable irq request generation */ | ||
198 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
199 | omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask); | ||
200 | |||
201 | /* Configure uplink threshold */ | ||
202 | if (downlink->threshold > DN_THRES_MAX) | ||
203 | downlink->threshold = DN_THRES_MAX; | ||
204 | |||
205 | omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold); | ||
206 | |||
207 | /* Enable DMA request generation */ | ||
208 | omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE); | ||
209 | |||
210 | /* Set pdm out format */ | ||
211 | ctrl = omap_mcpdm_read(MCPDM_CTRL); | ||
212 | ctrl &= ~PDMOUTFORMAT; | ||
213 | ctrl |= downlink->format & PDMOUTFORMAT; | ||
214 | |||
215 | /* Downlink channels */ | ||
216 | mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK); | ||
217 | |||
218 | omap_mcpdm_write(MCPDM_CTRL, ctrl); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Cleans McPDM uplink configuration. | ||
225 | * This function should be called when the stream is closed. | ||
226 | */ | ||
227 | int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink) | ||
228 | { | ||
229 | int irq_mask = 0; | ||
230 | |||
231 | if (!uplink) | ||
232 | return -EINVAL; | ||
233 | |||
234 | /* Disable irq request generation */ | ||
235 | irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK; | ||
236 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
237 | |||
238 | /* Disable DMA request generation */ | ||
239 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE); | ||
240 | |||
241 | /* Clear Downlink channels */ | ||
242 | mcpdm->up_channels = 0; | ||
243 | |||
244 | mcpdm->uplink = NULL; | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Cleans McPDM downlink configuration. | ||
251 | * This function should be called when the stream is closed. | ||
252 | */ | ||
253 | int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink) | ||
254 | { | ||
255 | int irq_mask = 0; | ||
256 | |||
257 | if (!downlink) | ||
258 | return -EINVAL; | ||
259 | |||
260 | /* Disable irq request generation */ | ||
261 | irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK; | ||
262 | omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask); | ||
263 | |||
264 | /* Disable DMA request generation */ | ||
265 | omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE); | ||
266 | |||
267 | /* clear Downlink channels */ | ||
268 | mcpdm->dn_channels = 0; | ||
269 | |||
270 | mcpdm->downlink = NULL; | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) | ||
276 | { | ||
277 | struct omap_mcpdm *mcpdm_irq = dev_id; | ||
278 | int irq_status; | ||
279 | |||
280 | irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS); | ||
281 | |||
282 | /* Acknowledge irq event */ | ||
283 | omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status); | ||
284 | |||
285 | if (irq & MCPDM_DN_IRQ_FULL) { | ||
286 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
287 | omap_mcpdm_reset_playback(1); | ||
288 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
289 | omap_mcpdm_reset_playback(0); | ||
290 | } | ||
291 | |||
292 | if (irq & MCPDM_DN_IRQ_EMPTY) { | ||
293 | dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status); | ||
294 | omap_mcpdm_reset_playback(1); | ||
295 | omap_mcpdm_playback_open(mcpdm_irq->downlink); | ||
296 | omap_mcpdm_reset_playback(0); | ||
297 | } | ||
298 | |||
299 | if (irq & MCPDM_DN_IRQ) { | ||
300 | dev_dbg(mcpdm_irq->dev, "DN write request\n"); | ||
301 | } | ||
302 | |||
303 | if (irq & MCPDM_UP_IRQ_FULL) { | ||
304 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
305 | omap_mcpdm_reset_capture(1); | ||
306 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
307 | omap_mcpdm_reset_capture(0); | ||
308 | } | ||
309 | |||
310 | if (irq & MCPDM_UP_IRQ_EMPTY) { | ||
311 | dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status); | ||
312 | omap_mcpdm_reset_capture(1); | ||
313 | omap_mcpdm_capture_open(mcpdm_irq->uplink); | ||
314 | omap_mcpdm_reset_capture(0); | ||
315 | } | ||
316 | |||
317 | if (irq & MCPDM_UP_IRQ) { | ||
318 | dev_dbg(mcpdm_irq->dev, "UP write request\n"); | ||
319 | } | ||
320 | |||
321 | return IRQ_HANDLED; | ||
322 | } | ||
323 | |||
324 | int omap_mcpdm_request(void) | ||
325 | { | ||
326 | int ret; | ||
327 | |||
328 | clk_enable(mcpdm->clk); | ||
329 | |||
330 | spin_lock(&mcpdm->lock); | ||
331 | |||
332 | if (!mcpdm->free) { | ||
333 | dev_err(mcpdm->dev, "McPDM interface is in use\n"); | ||
334 | spin_unlock(&mcpdm->lock); | ||
335 | ret = -EBUSY; | ||
336 | goto err; | ||
337 | } | ||
338 | mcpdm->free = 0; | ||
339 | |||
340 | spin_unlock(&mcpdm->lock); | ||
341 | |||
342 | /* Disable lines while request is ongoing */ | ||
343 | omap_mcpdm_write(MCPDM_CTRL, 0x00); | ||
344 | |||
345 | ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, | ||
346 | 0, "McPDM", (void *)mcpdm); | ||
347 | if (ret) { | ||
348 | dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n"); | ||
349 | goto err; | ||
350 | } | ||
351 | |||
352 | return 0; | ||
353 | |||
354 | err: | ||
355 | clk_disable(mcpdm->clk); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | void omap_mcpdm_free(void) | ||
360 | { | ||
361 | spin_lock(&mcpdm->lock); | ||
362 | if (mcpdm->free) { | ||
363 | dev_err(mcpdm->dev, "McPDM interface is already free\n"); | ||
364 | spin_unlock(&mcpdm->lock); | ||
365 | return; | ||
366 | } | ||
367 | mcpdm->free = 1; | ||
368 | spin_unlock(&mcpdm->lock); | ||
369 | |||
370 | clk_disable(mcpdm->clk); | ||
371 | |||
372 | free_irq(mcpdm->irq, (void *)mcpdm); | ||
373 | } | ||
374 | |||
375 | /* Enable/disable DC offset cancelation for the analog | ||
376 | * headset path (PDM channels 1 and 2). | ||
377 | */ | ||
378 | int omap_mcpdm_set_offset(int offset1, int offset2) | ||
379 | { | ||
380 | int offset; | ||
381 | |||
382 | if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX)) | ||
383 | return -EINVAL; | ||
384 | |||
385 | offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2); | ||
386 | |||
387 | /* offset cancellation for channel 1 */ | ||
388 | if (offset1) | ||
389 | offset |= DN_OFST_RX1_EN; | ||
390 | else | ||
391 | offset &= ~DN_OFST_RX1_EN; | ||
392 | |||
393 | /* offset cancellation for channel 2 */ | ||
394 | if (offset2) | ||
395 | offset |= DN_OFST_RX2_EN; | ||
396 | else | ||
397 | offset &= ~DN_OFST_RX2_EN; | ||
398 | |||
399 | omap_mcpdm_write(MCPDM_DN_OFFSET, offset); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int __devinit omap_mcpdm_probe(struct platform_device *pdev) | ||
405 | { | ||
406 | struct resource *res; | ||
407 | int ret = 0; | ||
408 | |||
409 | mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL); | ||
410 | if (!mcpdm) { | ||
411 | ret = -ENOMEM; | ||
412 | goto exit; | ||
413 | } | ||
414 | |||
415 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
416 | if (res == NULL) { | ||
417 | dev_err(&pdev->dev, "no resource\n"); | ||
418 | goto err_resource; | ||
419 | } | ||
420 | |||
421 | spin_lock_init(&mcpdm->lock); | ||
422 | mcpdm->free = 1; | ||
423 | mcpdm->io_base = ioremap(res->start, resource_size(res)); | ||
424 | if (!mcpdm->io_base) { | ||
425 | ret = -ENOMEM; | ||
426 | goto err_resource; | ||
427 | } | ||
428 | |||
429 | mcpdm->irq = platform_get_irq(pdev, 0); | ||
430 | |||
431 | mcpdm->clk = clk_get(&pdev->dev, "pdm_ck"); | ||
432 | if (IS_ERR(mcpdm->clk)) { | ||
433 | ret = PTR_ERR(mcpdm->clk); | ||
434 | dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret); | ||
435 | goto err_clk; | ||
436 | } | ||
437 | |||
438 | mcpdm->dev = &pdev->dev; | ||
439 | platform_set_drvdata(pdev, mcpdm); | ||
440 | |||
441 | return 0; | ||
442 | |||
443 | err_clk: | ||
444 | iounmap(mcpdm->io_base); | ||
445 | err_resource: | ||
446 | kfree(mcpdm); | ||
447 | exit: | ||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | static int __devexit omap_mcpdm_remove(struct platform_device *pdev) | ||
452 | { | ||
453 | struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev); | ||
454 | |||
455 | platform_set_drvdata(pdev, NULL); | ||
456 | |||
457 | clk_put(mcpdm_ptr->clk); | ||
458 | |||
459 | iounmap(mcpdm_ptr->io_base); | ||
460 | |||
461 | mcpdm_ptr->clk = NULL; | ||
462 | mcpdm_ptr->free = 0; | ||
463 | mcpdm_ptr->dev = NULL; | ||
464 | |||
465 | kfree(mcpdm_ptr); | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static struct platform_driver omap_mcpdm_driver = { | ||
471 | .probe = omap_mcpdm_probe, | ||
472 | .remove = __devexit_p(omap_mcpdm_remove), | ||
473 | .driver = { | ||
474 | .name = "omap-mcpdm", | ||
475 | }, | ||
476 | }; | ||
477 | |||
478 | static struct platform_device *omap_mcpdm_device; | ||
479 | |||
480 | static int __init omap_mcpdm_init(void) | ||
481 | { | ||
482 | return platform_driver_register(&omap_mcpdm_driver); | ||
483 | } | ||
484 | arch_initcall(omap_mcpdm_init); | ||
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h new file mode 100644 index 000000000000..7bb326ef0886 --- /dev/null +++ b/sound/soc/omap/mcpdm.h | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * mcpdm.h -- Defines for McPDM driver | ||
3 | * | ||
4 | * Author: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
18 | * 02110-1301 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* McPDM registers */ | ||
23 | |||
24 | #define MCPDM_REVISION 0x00 | ||
25 | #define MCPDM_SYSCONFIG 0x10 | ||
26 | #define MCPDM_IRQSTATUS_RAW 0x24 | ||
27 | #define MCPDM_IRQSTATUS 0x28 | ||
28 | #define MCPDM_IRQENABLE_SET 0x2C | ||
29 | #define MCPDM_IRQENABLE_CLR 0x30 | ||
30 | #define MCPDM_IRQWAKE_EN 0x34 | ||
31 | #define MCPDM_DMAENABLE_SET 0x38 | ||
32 | #define MCPDM_DMAENABLE_CLR 0x3C | ||
33 | #define MCPDM_DMAWAKEEN 0x40 | ||
34 | #define MCPDM_CTRL 0x44 | ||
35 | #define MCPDM_DN_DATA 0x48 | ||
36 | #define MCPDM_UP_DATA 0x4C | ||
37 | #define MCPDM_FIFO_CTRL_DN 0x50 | ||
38 | #define MCPDM_FIFO_CTRL_UP 0x54 | ||
39 | #define MCPDM_DN_OFFSET 0x58 | ||
40 | |||
41 | /* | ||
42 | * MCPDM_IRQ bit fields | ||
43 | * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR | ||
44 | */ | ||
45 | |||
46 | #define MCPDM_DN_IRQ (1 << 0) | ||
47 | #define MCPDM_DN_IRQ_EMPTY (1 << 1) | ||
48 | #define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2) | ||
49 | #define MCPDM_DN_IRQ_FULL (1 << 3) | ||
50 | |||
51 | #define MCPDM_UP_IRQ (1 << 8) | ||
52 | #define MCPDM_UP_IRQ_EMPTY (1 << 9) | ||
53 | #define MCPDM_UP_IRQ_ALMST_FULL (1 << 10) | ||
54 | #define MCPDM_UP_IRQ_FULL (1 << 11) | ||
55 | |||
56 | #define MCPDM_DOWNLINK_IRQ_MASK 0x00F | ||
57 | #define MCPDM_UPLINK_IRQ_MASK 0xF00 | ||
58 | |||
59 | /* | ||
60 | * MCPDM_DMAENABLE bit fields | ||
61 | */ | ||
62 | |||
63 | #define DMA_DN_ENABLE 0x1 | ||
64 | #define DMA_UP_ENABLE 0x2 | ||
65 | |||
66 | /* | ||
67 | * MCPDM_CTRL bit fields | ||
68 | */ | ||
69 | |||
70 | #define PDM_UP1_EN 0x0001 | ||
71 | #define PDM_UP2_EN 0x0002 | ||
72 | #define PDM_UP3_EN 0x0004 | ||
73 | #define PDM_DN1_EN 0x0008 | ||
74 | #define PDM_DN2_EN 0x0010 | ||
75 | #define PDM_DN3_EN 0x0020 | ||
76 | #define PDM_DN4_EN 0x0040 | ||
77 | #define PDM_DN5_EN 0x0080 | ||
78 | #define PDMOUTFORMAT 0x0100 | ||
79 | #define CMD_INT 0x0200 | ||
80 | #define STATUS_INT 0x0400 | ||
81 | #define SW_UP_RST 0x0800 | ||
82 | #define SW_DN_RST 0x1000 | ||
83 | #define PDM_UP_MASK 0x007 | ||
84 | #define PDM_DN_MASK 0x0F8 | ||
85 | #define PDM_CMD_MASK 0x200 | ||
86 | #define PDM_STATUS_MASK 0x400 | ||
87 | |||
88 | |||
89 | #define PDMOUTFORMAT_LJUST (0 << 8) | ||
90 | #define PDMOUTFORMAT_RJUST (1 << 8) | ||
91 | |||
92 | /* | ||
93 | * MCPDM_FIFO_CTRL bit fields | ||
94 | */ | ||
95 | |||
96 | #define UP_THRES_MAX 0xF | ||
97 | #define DN_THRES_MAX 0xF | ||
98 | |||
99 | /* | ||
100 | * MCPDM_DN_OFFSET bit fields | ||
101 | */ | ||
102 | |||
103 | #define DN_OFST_RX1_EN 0x0001 | ||
104 | #define DN_OFST_RX2_EN 0x0100 | ||
105 | |||
106 | #define DN_OFST_RX1 1 | ||
107 | #define DN_OFST_RX2 9 | ||
108 | #define DN_OFST_MAX 0x1F | ||
109 | |||
110 | #define MCPDM_UPLINK 1 | ||
111 | #define MCPDM_DOWNLINK 2 | ||
112 | |||
113 | struct omap_mcpdm_link { | ||
114 | int irq_mask; | ||
115 | int threshold; | ||
116 | int format; | ||
117 | int channels; | ||
118 | }; | ||
119 | |||
120 | struct omap_mcpdm_platform_data { | ||
121 | unsigned long phys_base; | ||
122 | u16 irq; | ||
123 | }; | ||
124 | |||
125 | struct omap_mcpdm { | ||
126 | struct device *dev; | ||
127 | unsigned long phys_base; | ||
128 | void __iomem *io_base; | ||
129 | u8 free; | ||
130 | int irq; | ||
131 | |||
132 | spinlock_t lock; | ||
133 | struct omap_mcpdm_platform_data *pdata; | ||
134 | struct clk *clk; | ||
135 | struct omap_mcpdm_link *downlink; | ||
136 | struct omap_mcpdm_link *uplink; | ||
137 | struct completion irq_completion; | ||
138 | |||
139 | int dn_channels; | ||
140 | int up_channels; | ||
141 | }; | ||
142 | |||
143 | extern void omap_mcpdm_start(int stream); | ||
144 | extern void omap_mcpdm_stop(int stream); | ||
145 | extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink); | ||
146 | extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink); | ||
147 | extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink); | ||
148 | extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink); | ||
149 | extern int omap_mcpdm_request(void); | ||
150 | extern void omap_mcpdm_free(void); | ||
151 | extern int omap_mcpdm_set_offset(int offset1, int offset2); | ||
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6bbbd2ab0ee7..e814a9591f78 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c | |||
@@ -39,6 +39,14 @@ | |||
39 | 39 | ||
40 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) | 40 | #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) |
41 | 41 | ||
42 | #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ | ||
43 | xhandler_get, xhandler_put) \ | ||
44 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
45 | .info = omap_mcbsp_st_info_volsw, \ | ||
46 | .get = xhandler_get, .put = xhandler_put, \ | ||
47 | .private_value = (unsigned long) &(struct soc_mixer_control) \ | ||
48 | {.min = xmin, .max = xmax} } | ||
49 | |||
42 | struct omap_mcbsp_data { | 50 | struct omap_mcbsp_data { |
43 | unsigned int bus_id; | 51 | unsigned int bus_id; |
44 | struct omap_mcbsp_reg_cfg regs; | 52 | struct omap_mcbsp_reg_cfg regs; |
@@ -82,11 +90,11 @@ static const int omap1_dma_reqs[][2] = {}; | |||
82 | static const unsigned long omap1_mcbsp_port[][2] = {}; | 90 | static const unsigned long omap1_mcbsp_port[][2] = {}; |
83 | #endif | 91 | #endif |
84 | 92 | ||
85 | #if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) | 93 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
86 | static const int omap24xx_dma_reqs[][2] = { | 94 | static const int omap24xx_dma_reqs[][2] = { |
87 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, | 95 | { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, |
88 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, | 96 | { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, |
89 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | 97 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) |
90 | { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, | 98 | { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, |
91 | { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, | 99 | { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, |
92 | { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, | 100 | { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, |
@@ -124,7 +132,7 @@ static const unsigned long omap2430_mcbsp_port[][2] = { | |||
124 | static const unsigned long omap2430_mcbsp_port[][2] = {}; | 132 | static const unsigned long omap2430_mcbsp_port[][2] = {}; |
125 | #endif | 133 | #endif |
126 | 134 | ||
127 | #if defined(CONFIG_ARCH_OMAP34XX) | 135 | #if defined(CONFIG_ARCH_OMAP3) |
128 | static const unsigned long omap34xx_mcbsp_port[][2] = { | 136 | static const unsigned long omap34xx_mcbsp_port[][2] = { |
129 | { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, | 137 | { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, |
130 | OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, | 138 | OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, |
@@ -287,6 +295,8 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, | |||
287 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; | 295 | omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; |
288 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; | 296 | omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; |
289 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; | 297 | omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode; |
298 | omap_mcbsp_dai_dma_params[id][substream->stream].data_type = | ||
299 | OMAP_DMA_DATA_TYPE_S16; | ||
290 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; | 300 | cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; |
291 | 301 | ||
292 | if (mcbsp_data->configured) { | 302 | if (mcbsp_data->configured) { |
@@ -637,6 +647,136 @@ struct snd_soc_dai omap_mcbsp_dai[] = { | |||
637 | 647 | ||
638 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); | 648 | EXPORT_SYMBOL_GPL(omap_mcbsp_dai); |
639 | 649 | ||
650 | int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, | ||
651 | struct snd_ctl_elem_info *uinfo) | ||
652 | { | ||
653 | struct soc_mixer_control *mc = | ||
654 | (struct soc_mixer_control *)kcontrol->private_value; | ||
655 | int max = mc->max; | ||
656 | int min = mc->min; | ||
657 | |||
658 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
659 | uinfo->count = 1; | ||
660 | uinfo->value.integer.min = min; | ||
661 | uinfo->value.integer.max = max; | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | #define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \ | ||
666 | static int \ | ||
667 | omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
668 | struct snd_ctl_elem_value *uc) \ | ||
669 | { \ | ||
670 | struct soc_mixer_control *mc = \ | ||
671 | (struct soc_mixer_control *)kc->private_value; \ | ||
672 | int max = mc->max; \ | ||
673 | int min = mc->min; \ | ||
674 | int val = uc->value.integer.value[0]; \ | ||
675 | \ | ||
676 | if (val < min || val > max) \ | ||
677 | return -EINVAL; \ | ||
678 | \ | ||
679 | /* OMAP McBSP implementation uses index values 0..4 */ \ | ||
680 | return omap_st_set_chgain((id)-1, channel, val); \ | ||
681 | } | ||
682 | |||
683 | #define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \ | ||
684 | static int \ | ||
685 | omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ | ||
686 | struct snd_ctl_elem_value *uc) \ | ||
687 | { \ | ||
688 | s16 chgain; \ | ||
689 | \ | ||
690 | if (omap_st_get_chgain((id)-1, channel, &chgain)) \ | ||
691 | return -EAGAIN; \ | ||
692 | \ | ||
693 | uc->value.integer.value[0] = chgain; \ | ||
694 | return 0; \ | ||
695 | } | ||
696 | |||
697 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0) | ||
698 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1) | ||
699 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0) | ||
700 | OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1) | ||
701 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0) | ||
702 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1) | ||
703 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0) | ||
704 | OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1) | ||
705 | |||
706 | static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, | ||
707 | struct snd_ctl_elem_value *ucontrol) | ||
708 | { | ||
709 | struct soc_mixer_control *mc = | ||
710 | (struct soc_mixer_control *)kcontrol->private_value; | ||
711 | u8 value = ucontrol->value.integer.value[0]; | ||
712 | |||
713 | if (value == omap_st_is_enabled(mc->reg)) | ||
714 | return 0; | ||
715 | |||
716 | if (value) | ||
717 | omap_st_enable(mc->reg); | ||
718 | else | ||
719 | omap_st_disable(mc->reg); | ||
720 | |||
721 | return 1; | ||
722 | } | ||
723 | |||
724 | static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, | ||
725 | struct snd_ctl_elem_value *ucontrol) | ||
726 | { | ||
727 | struct soc_mixer_control *mc = | ||
728 | (struct soc_mixer_control *)kcontrol->private_value; | ||
729 | |||
730 | ucontrol->value.integer.value[0] = omap_st_is_enabled(mc->reg); | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = { | ||
735 | SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 1, 0, 1, 0, | ||
736 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), | ||
737 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume", | ||
738 | -32768, 32767, | ||
739 | omap_mcbsp2_get_st_ch0_volume, | ||
740 | omap_mcbsp2_set_st_ch0_volume), | ||
741 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume", | ||
742 | -32768, 32767, | ||
743 | omap_mcbsp2_get_st_ch1_volume, | ||
744 | omap_mcbsp2_set_st_ch1_volume), | ||
745 | }; | ||
746 | |||
747 | static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = { | ||
748 | SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 2, 0, 1, 0, | ||
749 | omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), | ||
750 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume", | ||
751 | -32768, 32767, | ||
752 | omap_mcbsp3_get_st_ch0_volume, | ||
753 | omap_mcbsp3_set_st_ch0_volume), | ||
754 | OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume", | ||
755 | -32768, 32767, | ||
756 | omap_mcbsp3_get_st_ch1_volume, | ||
757 | omap_mcbsp3_set_st_ch1_volume), | ||
758 | }; | ||
759 | |||
760 | int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id) | ||
761 | { | ||
762 | if (!cpu_is_omap34xx()) | ||
763 | return -ENODEV; | ||
764 | |||
765 | switch (mcbsp_id) { | ||
766 | case 1: /* McBSP 2 */ | ||
767 | return snd_soc_add_controls(codec, omap_mcbsp2_st_controls, | ||
768 | ARRAY_SIZE(omap_mcbsp2_st_controls)); | ||
769 | case 2: /* McBSP 3 */ | ||
770 | return snd_soc_add_controls(codec, omap_mcbsp3_st_controls, | ||
771 | ARRAY_SIZE(omap_mcbsp3_st_controls)); | ||
772 | default: | ||
773 | break; | ||
774 | } | ||
775 | |||
776 | return -EINVAL; | ||
777 | } | ||
778 | EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); | ||
779 | |||
640 | static int __init snd_omap_mcbsp_init(void) | 780 | static int __init snd_omap_mcbsp_init(void) |
641 | { | 781 | { |
642 | return snd_soc_register_dais(omap_mcbsp_dai, | 782 | return snd_soc_register_dais(omap_mcbsp_dai, |
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 647d2f981ab0..6c363e5f4387 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h | |||
@@ -50,11 +50,13 @@ enum omap_mcbsp_div { | |||
50 | #undef NUM_LINKS | 50 | #undef NUM_LINKS |
51 | #define NUM_LINKS 3 | 51 | #define NUM_LINKS 3 |
52 | #endif | 52 | #endif |
53 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) | 53 | #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) |
54 | #undef NUM_LINKS | 54 | #undef NUM_LINKS |
55 | #define NUM_LINKS 5 | 55 | #define NUM_LINKS 5 |
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; | 58 | extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; |
59 | 59 | ||
60 | int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id); | ||
61 | |||
60 | #endif | 62 | #endif |
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c new file mode 100644 index 000000000000..25f19e4728bf --- /dev/null +++ b/sound/soc/omap/omap-mcpdm.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port | ||
3 | * | ||
4 | * Copyright (C) 2009 Texas Instruments | ||
5 | * | ||
6 | * Author: Misael Lopez Cruz <x0052729@ti.com> | ||
7 | * Contact: Jorge Eduardo Candelaria <x0107209@ti.com> | ||
8 | * Margarita Olaya <magi.olaya@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 | #include <linux/init.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/pcm_params.h> | ||
32 | #include <sound/initval.h> | ||
33 | #include <sound/soc.h> | ||
34 | |||
35 | #include <plat/control.h> | ||
36 | #include <plat/dma.h> | ||
37 | #include <plat/mcbsp.h> | ||
38 | #include "mcpdm.h" | ||
39 | #include "omap-mcpdm.h" | ||
40 | #include "omap-pcm.h" | ||
41 | |||
42 | struct omap_mcpdm_data { | ||
43 | struct omap_mcpdm_link *links; | ||
44 | int active; | ||
45 | }; | ||
46 | |||
47 | static struct omap_mcpdm_link omap_mcpdm_links[] = { | ||
48 | /* downlink */ | ||
49 | { | ||
50 | .irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL, | ||
51 | .threshold = 1, | ||
52 | .format = PDMOUTFORMAT_LJUST, | ||
53 | }, | ||
54 | /* uplink */ | ||
55 | { | ||
56 | .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL, | ||
57 | .threshold = 1, | ||
58 | .format = PDMOUTFORMAT_LJUST, | ||
59 | }, | ||
60 | }; | ||
61 | |||
62 | static struct omap_mcpdm_data mcpdm_data = { | ||
63 | .links = omap_mcpdm_links, | ||
64 | .active = 0, | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * Stream DMA parameters | ||
69 | */ | ||
70 | static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { | ||
71 | { | ||
72 | .name = "Audio playback", | ||
73 | .dma_req = OMAP44XX_DMA_MCPDM_DL, | ||
74 | .data_type = OMAP_DMA_DATA_TYPE_S32, | ||
75 | .sync_mode = OMAP_DMA_SYNC_PACKET, | ||
76 | .packet_size = 16, | ||
77 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA, | ||
78 | }, | ||
79 | { | ||
80 | .name = "Audio capture", | ||
81 | .dma_req = OMAP44XX_DMA_MCPDM_UP, | ||
82 | .data_type = OMAP_DMA_DATA_TYPE_S32, | ||
83 | .sync_mode = OMAP_DMA_SYNC_PACKET, | ||
84 | .packet_size = 16, | ||
85 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA, | ||
86 | }, | ||
87 | }; | ||
88 | |||
89 | static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, | ||
90 | struct snd_soc_dai *dai) | ||
91 | { | ||
92 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
93 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
94 | int err = 0; | ||
95 | |||
96 | if (!cpu_dai->active) | ||
97 | err = omap_mcpdm_request(); | ||
98 | |||
99 | return err; | ||
100 | } | ||
101 | |||
102 | static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, | ||
103 | struct snd_soc_dai *dai) | ||
104 | { | ||
105 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
106 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
107 | |||
108 | if (!cpu_dai->active) | ||
109 | omap_mcpdm_free(); | ||
110 | } | ||
111 | |||
112 | static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
113 | struct snd_soc_dai *dai) | ||
114 | { | ||
115 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
116 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
117 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
118 | int stream = substream->stream; | ||
119 | int err = 0; | ||
120 | |||
121 | switch (cmd) { | ||
122 | case SNDRV_PCM_TRIGGER_START: | ||
123 | case SNDRV_PCM_TRIGGER_RESUME: | ||
124 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
125 | if (!mcpdm_priv->active++) | ||
126 | omap_mcpdm_start(stream); | ||
127 | break; | ||
128 | |||
129 | case SNDRV_PCM_TRIGGER_STOP: | ||
130 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
131 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
132 | if (!--mcpdm_priv->active) | ||
133 | omap_mcpdm_stop(stream); | ||
134 | break; | ||
135 | default: | ||
136 | err = -EINVAL; | ||
137 | } | ||
138 | |||
139 | return err; | ||
140 | } | ||
141 | |||
142 | static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, | ||
143 | struct snd_pcm_hw_params *params, | ||
144 | struct snd_soc_dai *dai) | ||
145 | { | ||
146 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
147 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
148 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
149 | struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; | ||
150 | int stream = substream->stream; | ||
151 | int channels, err, link_mask = 0; | ||
152 | |||
153 | cpu_dai->dma_data = &omap_mcpdm_dai_dma_params[stream]; | ||
154 | |||
155 | channels = params_channels(params); | ||
156 | switch (channels) { | ||
157 | case 4: | ||
158 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
159 | /* up to 2 channels for capture */ | ||
160 | return -EINVAL; | ||
161 | link_mask |= 1 << 3; | ||
162 | case 3: | ||
163 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
164 | /* up to 2 channels for capture */ | ||
165 | return -EINVAL; | ||
166 | link_mask |= 1 << 2; | ||
167 | case 2: | ||
168 | link_mask |= 1 << 1; | ||
169 | case 1: | ||
170 | link_mask |= 1 << 0; | ||
171 | break; | ||
172 | default: | ||
173 | /* unsupported number of channels */ | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
178 | mcpdm_links[stream].channels = link_mask << 3; | ||
179 | err = omap_mcpdm_playback_open(&mcpdm_links[stream]); | ||
180 | } else { | ||
181 | mcpdm_links[stream].channels = link_mask << 0; | ||
182 | err = omap_mcpdm_capture_open(&mcpdm_links[stream]); | ||
183 | } | ||
184 | |||
185 | return err; | ||
186 | } | ||
187 | |||
188 | static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream, | ||
189 | struct snd_soc_dai *dai) | ||
190 | { | ||
191 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
192 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
193 | struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; | ||
194 | struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; | ||
195 | int stream = substream->stream; | ||
196 | int err; | ||
197 | |||
198 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
199 | err = omap_mcpdm_playback_close(&mcpdm_links[stream]); | ||
200 | else | ||
201 | err = omap_mcpdm_capture_close(&mcpdm_links[stream]); | ||
202 | |||
203 | return err; | ||
204 | } | ||
205 | |||
206 | static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { | ||
207 | .startup = omap_mcpdm_dai_startup, | ||
208 | .shutdown = omap_mcpdm_dai_shutdown, | ||
209 | .trigger = omap_mcpdm_dai_trigger, | ||
210 | .hw_params = omap_mcpdm_dai_hw_params, | ||
211 | .hw_free = omap_mcpdm_dai_hw_free, | ||
212 | }; | ||
213 | |||
214 | #define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
215 | #define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) | ||
216 | |||
217 | struct snd_soc_dai omap_mcpdm_dai = { | ||
218 | .name = "omap-mcpdm", | ||
219 | .id = -1, | ||
220 | .playback = { | ||
221 | .channels_min = 1, | ||
222 | .channels_max = 4, | ||
223 | .rates = OMAP_MCPDM_RATES, | ||
224 | .formats = OMAP_MCPDM_FORMATS, | ||
225 | }, | ||
226 | .capture = { | ||
227 | .channels_min = 1, | ||
228 | .channels_max = 2, | ||
229 | .rates = OMAP_MCPDM_RATES, | ||
230 | .formats = OMAP_MCPDM_FORMATS, | ||
231 | }, | ||
232 | .ops = &omap_mcpdm_dai_ops, | ||
233 | .private_data = &mcpdm_data, | ||
234 | }; | ||
235 | EXPORT_SYMBOL_GPL(omap_mcpdm_dai); | ||
236 | |||
237 | static int __init snd_omap_mcpdm_init(void) | ||
238 | { | ||
239 | return snd_soc_register_dai(&omap_mcpdm_dai); | ||
240 | } | ||
241 | module_init(snd_omap_mcpdm_init); | ||
242 | |||
243 | static void __exit snd_omap_mcpdm_exit(void) | ||
244 | { | ||
245 | snd_soc_unregister_dai(&omap_mcpdm_dai); | ||
246 | } | ||
247 | module_exit(snd_omap_mcpdm_exit); | ||
248 | |||
249 | MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); | ||
250 | MODULE_DESCRIPTION("OMAP PDM SoC Interface"); | ||
251 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h new file mode 100644 index 000000000000..73b80d559345 --- /dev/null +++ b/sound/soc/omap/omap-mcpdm.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * omap-mcpdm.h | ||
3 | * | ||
4 | * Copyright (C) 2009 Texas Instruments | ||
5 | * | ||
6 | * Contact: Misael Lopez Cruz <x0052729@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #ifndef __OMAP_MCPDM_H__ | ||
25 | #define __OMAP_MCPDM_H__ | ||
26 | |||
27 | extern struct snd_soc_dai omap_mcpdm_dai; | ||
28 | |||
29 | #endif /* End of __OMAP_MCPDM_H__ */ | ||
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 9db2770e9640..825db385f01f 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c | |||
@@ -37,7 +37,8 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { | |||
37 | SNDRV_PCM_INFO_INTERLEAVED | | 37 | SNDRV_PCM_INFO_INTERLEAVED | |
38 | SNDRV_PCM_INFO_PAUSE | | 38 | SNDRV_PCM_INFO_PAUSE | |
39 | SNDRV_PCM_INFO_RESUME, | 39 | SNDRV_PCM_INFO_RESUME, |
40 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 40 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
41 | SNDRV_PCM_FMTBIT_S32_LE, | ||
41 | .period_bytes_min = 32, | 42 | .period_bytes_min = 32, |
42 | .period_bytes_max = 64 * 1024, | 43 | .period_bytes_max = 64 * 1024, |
43 | .periods_min = 2, | 44 | .periods_min = 2, |
@@ -149,6 +150,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
149 | struct omap_runtime_data *prtd = runtime->private_data; | 150 | struct omap_runtime_data *prtd = runtime->private_data; |
150 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; | 151 | struct omap_pcm_dma_data *dma_data = prtd->dma_data; |
151 | struct omap_dma_channel_params dma_params; | 152 | struct omap_dma_channel_params dma_params; |
153 | int bytes; | ||
152 | 154 | ||
153 | /* return if this is a bufferless transfer e.g. | 155 | /* return if this is a bufferless transfer e.g. |
154 | * codec <--> BT codec or GSM modem -- lg FIXME */ | 156 | * codec <--> BT codec or GSM modem -- lg FIXME */ |
@@ -156,11 +158,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
156 | return 0; | 158 | return 0; |
157 | 159 | ||
158 | memset(&dma_params, 0, sizeof(dma_params)); | 160 | memset(&dma_params, 0, sizeof(dma_params)); |
159 | /* | 161 | dma_params.data_type = dma_data->data_type; |
160 | * Note: Regardless of interface data formats supported by OMAP McBSP | ||
161 | * or EAC blocks, internal representation is always fixed 16-bit/sample | ||
162 | */ | ||
163 | dma_params.data_type = OMAP_DMA_DATA_TYPE_S16; | ||
164 | dma_params.trigger = dma_data->dma_req; | 162 | dma_params.trigger = dma_data->dma_req; |
165 | dma_params.sync_mode = dma_data->sync_mode; | 163 | dma_params.sync_mode = dma_data->sync_mode; |
166 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 164 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
@@ -170,6 +168,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
170 | dma_params.src_start = runtime->dma_addr; | 168 | dma_params.src_start = runtime->dma_addr; |
171 | dma_params.dst_start = dma_data->port_addr; | 169 | dma_params.dst_start = dma_data->port_addr; |
172 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; | 170 | dma_params.dst_port = OMAP_DMA_PORT_MPUI; |
171 | dma_params.dst_fi = dma_data->packet_size; | ||
173 | } else { | 172 | } else { |
174 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; | 173 | dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; |
175 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; | 174 | dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; |
@@ -177,6 +176,7 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
177 | dma_params.src_start = dma_data->port_addr; | 176 | dma_params.src_start = dma_data->port_addr; |
178 | dma_params.dst_start = runtime->dma_addr; | 177 | dma_params.dst_start = runtime->dma_addr; |
179 | dma_params.src_port = OMAP_DMA_PORT_MPUI; | 178 | dma_params.src_port = OMAP_DMA_PORT_MPUI; |
179 | dma_params.src_fi = dma_data->packet_size; | ||
180 | } | 180 | } |
181 | /* | 181 | /* |
182 | * Set DMA transfer frame size equal to ALSA period size and frame | 182 | * Set DMA transfer frame size equal to ALSA period size and frame |
@@ -184,7 +184,8 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) | |||
184 | * we can transfer the whole ALSA buffer with single DMA transfer but | 184 | * we can transfer the whole ALSA buffer with single DMA transfer but |
185 | * still can get an interrupt at each period bounary | 185 | * still can get an interrupt at each period bounary |
186 | */ | 186 | */ |
187 | dma_params.elem_count = snd_pcm_lib_period_bytes(substream) / 2; | 187 | bytes = snd_pcm_lib_period_bytes(substream); |
188 | dma_params.elem_count = bytes >> dma_data->data_type; | ||
188 | dma_params.frame_count = runtime->periods; | 189 | dma_params.frame_count = runtime->periods; |
189 | omap_set_dma_params(prtd->dma_ch, &dma_params); | 190 | omap_set_dma_params(prtd->dma_ch, &dma_params); |
190 | 191 | ||
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h index 38a821dd4118..b19975d26907 100644 --- a/sound/soc/omap/omap-pcm.h +++ b/sound/soc/omap/omap-pcm.h | |||
@@ -29,8 +29,10 @@ struct omap_pcm_dma_data { | |||
29 | char *name; /* stream identifier */ | 29 | char *name; /* stream identifier */ |
30 | int dma_req; /* DMA request line */ | 30 | int dma_req; /* DMA request line */ |
31 | unsigned long port_addr; /* transmit/receive register */ | 31 | unsigned long port_addr; /* transmit/receive register */ |
32 | int sync_mode; /* DMA sync mode */ | ||
33 | void (*set_threshold)(struct snd_pcm_substream *substream); | 32 | void (*set_threshold)(struct snd_pcm_substream *substream); |
33 | int data_type; /* data type 8,16,32 */ | ||
34 | int sync_mode; /* DMA sync mode */ | ||
35 | int packet_size; /* packet size only in PACKET mode */ | ||
34 | }; | 36 | }; |
35 | 37 | ||
36 | extern struct snd_soc_platform omap_soc_platform; | 38 | extern struct snd_soc_platform omap_soc_platform; |
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c index d88ad5ca526c..240e0975dd6a 100644 --- a/sound/soc/omap/omap3beagle.c +++ b/sound/soc/omap/omap3beagle.c | |||
@@ -117,11 +117,11 @@ static int __init omap3beagle_soc_init(void) | |||
117 | { | 117 | { |
118 | int ret; | 118 | int ret; |
119 | 119 | ||
120 | if (!machine_is_omap3_beagle()) { | 120 | if (!(machine_is_omap3_beagle() || machine_is_devkit8000())) { |
121 | pr_debug("Not OMAP3 Beagle!\n"); | 121 | pr_debug("Not OMAP3 Beagle or Devkit8000!\n"); |
122 | return -ENODEV; | 122 | return -ENODEV; |
123 | } | 123 | } |
124 | pr_info("OMAP3 Beagle SoC init\n"); | 124 | pr_info("OMAP3 Beagle/Devkit8000 SoC init\n"); |
125 | 125 | ||
126 | omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); | 126 | omap3beagle_snd_device = platform_device_alloc("soc-audio", -1); |
127 | if (!omap3beagle_snd_device) { | 127 | if (!omap3beagle_snd_device) { |
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c index 68980c19a3bc..de10f76baded 100644 --- a/sound/soc/omap/omap3pandora.c +++ b/sound/soc/omap/omap3pandora.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | #include <linux/gpio.h> | 24 | #include <linux/gpio.h> |
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/regulator/consumer.h> | ||
26 | 27 | ||
27 | #include <sound/core.h> | 28 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
@@ -40,6 +41,8 @@ | |||
40 | 41 | ||
41 | #define PREFIX "ASoC omap3pandora: " | 42 | #define PREFIX "ASoC omap3pandora: " |
42 | 43 | ||
44 | static struct regulator *omap3pandora_dac_reg; | ||
45 | |||
43 | static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, | 46 | static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream, |
44 | struct snd_pcm_hw_params *params, unsigned int fmt) | 47 | struct snd_pcm_hw_params *params, unsigned int fmt) |
45 | { | 48 | { |
@@ -106,21 +109,37 @@ static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream, | |||
106 | SND_SOC_DAIFMT_CBS_CFS); | 109 | SND_SOC_DAIFMT_CBS_CFS); |
107 | } | 110 | } |
108 | 111 | ||
109 | static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, | 112 | static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, |
110 | struct snd_kcontrol *k, int event) | 113 | struct snd_kcontrol *k, int event) |
111 | { | 114 | { |
115 | /* | ||
116 | * The PCM1773 DAC datasheet requires 1ms delay between switching | ||
117 | * VCC power on/off and /PD pin high/low | ||
118 | */ | ||
112 | if (SND_SOC_DAPM_EVENT_ON(event)) { | 119 | if (SND_SOC_DAPM_EVENT_ON(event)) { |
120 | regulator_enable(omap3pandora_dac_reg); | ||
121 | mdelay(1); | ||
113 | gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); | 122 | gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); |
114 | gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); | ||
115 | } else { | 123 | } else { |
116 | gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); | ||
117 | mdelay(1); | ||
118 | gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); | 124 | gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); |
125 | mdelay(1); | ||
126 | regulator_disable(omap3pandora_dac_reg); | ||
119 | } | 127 | } |
120 | 128 | ||
121 | return 0; | 129 | return 0; |
122 | } | 130 | } |
123 | 131 | ||
132 | static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, | ||
133 | struct snd_kcontrol *k, int event) | ||
134 | { | ||
135 | if (SND_SOC_DAPM_EVENT_ON(event)) | ||
136 | gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); | ||
137 | else | ||
138 | gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
124 | /* | 143 | /* |
125 | * Audio paths on Pandora board: | 144 | * Audio paths on Pandora board: |
126 | * | 145 | * |
@@ -130,7 +149,9 @@ static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, | |||
130 | * |P| <--- TWL4030 <--------- Line In and MICs | 149 | * |P| <--- TWL4030 <--------- Line In and MICs |
131 | */ | 150 | */ |
132 | static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = { | 151 | static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = { |
133 | SND_SOC_DAPM_DAC("PCM DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0), | 152 | SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM, |
153 | 0, 0, omap3pandora_dac_event, | ||
154 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | ||
134 | SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, | 155 | SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, |
135 | 0, 0, NULL, 0, omap3pandora_hp_event, | 156 | 0, 0, NULL, 0, omap3pandora_hp_event, |
136 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), | 157 | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), |
@@ -306,8 +327,18 @@ static int __init omap3pandora_soc_init(void) | |||
306 | goto fail2; | 327 | goto fail2; |
307 | } | 328 | } |
308 | 329 | ||
330 | omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); | ||
331 | if (IS_ERR(omap3pandora_dac_reg)) { | ||
332 | pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", | ||
333 | dev_name(&omap3pandora_snd_device->dev), | ||
334 | PTR_ERR(omap3pandora_dac_reg)); | ||
335 | goto fail3; | ||
336 | } | ||
337 | |||
309 | return 0; | 338 | return 0; |
310 | 339 | ||
340 | fail3: | ||
341 | platform_device_del(omap3pandora_snd_device); | ||
311 | fail2: | 342 | fail2: |
312 | platform_device_put(omap3pandora_snd_device); | 343 | platform_device_put(omap3pandora_snd_device); |
313 | fail1: | 344 | fail1: |
@@ -320,6 +351,7 @@ module_init(omap3pandora_soc_init); | |||
320 | 351 | ||
321 | static void __exit omap3pandora_soc_exit(void) | 352 | static void __exit omap3pandora_soc_exit(void) |
322 | { | 353 | { |
354 | regulator_put(omap3pandora_dac_reg); | ||
323 | platform_device_unregister(omap3pandora_snd_device); | 355 | platform_device_unregister(omap3pandora_snd_device); |
324 | gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); | 356 | gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); |
325 | gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); | 357 | gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); |