aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/omap
diff options
context:
space:
mode:
authorMisael Lopez Cruz <misael.lopez@ti.com>2011-07-05 12:50:45 -0400
committerPeter Ujfalusi <peter.ujfalusi@ti.com>2011-09-22 02:22:50 -0400
commitf5f9d7bf6eb2efc6e819550b70a3292d83acc6eb (patch)
tree3e65ff6f476f06bef77fb25ee93917e8e3a50c10 /sound/soc/omap
parent3a98cd6b2b0459de4ebf85356fbcc34b9848b58a (diff)
ASoC: omap-mcpdm: Replace legacy driver
Reasons for the replacement: The current driver for McPDM was developed to support the legacy mode only. In preparation for the ABE support the current driver stack need the be replaced. The new driver is much simpler, easier to extend, and it also fixes some of the issues with the old stack. Main changes: - single file for omap-mcpdm (mcpdm.c/h removed) - Define names for registers, bits cleaned up, prefixed - Full-duplex audio operation (arecord | aplay) has been fixed - Less code McPDM need to be turned off after all streams has been stopped. This might cause pop noise on the output, if the codec's DAC is still powered at this time. Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com> Signed-off-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Sebastien Guiriec <s-guiriec@ti.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/omap')
-rw-r--r--sound/soc/omap/Makefile2
-rw-r--r--sound/soc/omap/mcpdm.c474
-rw-r--r--sound/soc/omap/mcpdm.h152
-rw-r--r--sound/soc/omap/omap-mcpdm.c443
-rw-r--r--sound/soc/omap/omap-mcpdm.h95
-rw-r--r--sound/soc/omap/sdp4430.c2
6 files changed, 434 insertions, 734 deletions
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 59e2c8d1e38d..052fd758722e 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -1,7 +1,7 @@
1# OMAP Platform Support 1# OMAP Platform Support
2snd-soc-omap-objs := omap-pcm.o 2snd-soc-omap-objs := omap-pcm.o
3snd-soc-omap-mcbsp-objs := omap-mcbsp.o 3snd-soc-omap-mcbsp-objs := omap-mcbsp.o
4snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o 4snd-soc-omap-mcpdm-objs := omap-mcpdm.o
5snd-soc-omap-hdmi-objs := omap-hdmi.o 5snd-soc-omap-hdmi-objs := omap-hdmi.o
6 6
7obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o 7obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
deleted file mode 100644
index d29cc982b562..000000000000
--- a/sound/soc/omap/mcpdm.c
+++ /dev/null
@@ -1,474 +0,0 @@
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/slab.h>
29#include <linux/interrupt.h>
30#include <linux/err.h>
31#include <linux/pm_runtime.h>
32#include <linux/delay.h>
33#include <linux/io.h>
34#include <linux/irq.h>
35
36#include "mcpdm.h"
37
38static struct omap_mcpdm *mcpdm;
39
40static inline void omap_mcpdm_write(u16 reg, u32 val)
41{
42 __raw_writel(val, mcpdm->io_base + reg);
43}
44
45static inline int omap_mcpdm_read(u16 reg)
46{
47 return __raw_readl(mcpdm->io_base + reg);
48}
49
50static void omap_mcpdm_reg_dump(void)
51{
52 dev_dbg(mcpdm->dev, "***********************\n");
53 dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
54 omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
55 dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
56 omap_mcpdm_read(MCPDM_IRQSTATUS));
57 dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
58 omap_mcpdm_read(MCPDM_IRQENABLE_SET));
59 dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
60 omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
61 dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
62 omap_mcpdm_read(MCPDM_IRQWAKE_EN));
63 dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
64 omap_mcpdm_read(MCPDM_DMAENABLE_SET));
65 dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
66 omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
67 dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
68 omap_mcpdm_read(MCPDM_DMAWAKEEN));
69 dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
70 omap_mcpdm_read(MCPDM_CTRL));
71 dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
72 omap_mcpdm_read(MCPDM_DN_DATA));
73 dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
74 omap_mcpdm_read(MCPDM_UP_DATA));
75 dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
76 omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
77 dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
78 omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
79 dev_dbg(mcpdm->dev, "DN_OFFSET: 0x%04x\n",
80 omap_mcpdm_read(MCPDM_DN_OFFSET));
81 dev_dbg(mcpdm->dev, "***********************\n");
82}
83
84/*
85 * Takes the McPDM module in and out of reset state.
86 * Uplink and downlink can be reset individually.
87 */
88static void omap_mcpdm_reset_capture(int reset)
89{
90 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
91
92 if (reset)
93 ctrl |= SW_UP_RST;
94 else
95 ctrl &= ~SW_UP_RST;
96
97 omap_mcpdm_write(MCPDM_CTRL, ctrl);
98}
99
100static void omap_mcpdm_reset_playback(int reset)
101{
102 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
103
104 if (reset)
105 ctrl |= SW_DN_RST;
106 else
107 ctrl &= ~SW_DN_RST;
108
109 omap_mcpdm_write(MCPDM_CTRL, ctrl);
110}
111
112/*
113 * Enables the transfer through the PDM interface to/from the Phoenix
114 * codec by enabling the corresponding UP or DN channels.
115 */
116void omap_mcpdm_start(int stream)
117{
118 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
119
120 if (stream)
121 ctrl |= mcpdm->up_channels;
122 else
123 ctrl |= mcpdm->dn_channels;
124
125 omap_mcpdm_write(MCPDM_CTRL, ctrl);
126}
127
128/*
129 * Disables the transfer through the PDM interface to/from the Phoenix
130 * codec by disabling the corresponding UP or DN channels.
131 */
132void omap_mcpdm_stop(int stream)
133{
134 int ctrl = omap_mcpdm_read(MCPDM_CTRL);
135
136 if (stream)
137 ctrl &= ~mcpdm->up_channels;
138 else
139 ctrl &= ~mcpdm->dn_channels;
140
141 omap_mcpdm_write(MCPDM_CTRL, ctrl);
142}
143
144/*
145 * Configures McPDM uplink for audio recording.
146 * This function should be called before omap_mcpdm_start.
147 */
148int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink)
149{
150 int irq_mask = 0;
151 int ctrl;
152
153 if (!uplink)
154 return -EINVAL;
155
156 mcpdm->uplink = uplink;
157
158 /* Enable irq request generation */
159 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
160 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
161
162 /* Configure uplink threshold */
163 if (uplink->threshold > UP_THRES_MAX)
164 uplink->threshold = UP_THRES_MAX;
165
166 omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
167
168 /* Configure DMA controller */
169 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
170
171 /* Set pdm out format */
172 ctrl = omap_mcpdm_read(MCPDM_CTRL);
173 ctrl &= ~PDMOUTFORMAT;
174 ctrl |= uplink->format & PDMOUTFORMAT;
175
176 /* Uplink channels */
177 mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
178
179 omap_mcpdm_write(MCPDM_CTRL, ctrl);
180
181 return 0;
182}
183
184/*
185 * Configures McPDM downlink for audio playback.
186 * This function should be called before omap_mcpdm_start.
187 */
188int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink)
189{
190 int irq_mask = 0;
191 int ctrl;
192
193 if (!downlink)
194 return -EINVAL;
195
196 mcpdm->downlink = downlink;
197
198 /* Enable irq request generation */
199 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
200 omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
201
202 /* Configure uplink threshold */
203 if (downlink->threshold > DN_THRES_MAX)
204 downlink->threshold = DN_THRES_MAX;
205
206 omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
207
208 /* Enable DMA request generation */
209 omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
210
211 /* Set pdm out format */
212 ctrl = omap_mcpdm_read(MCPDM_CTRL);
213 ctrl &= ~PDMOUTFORMAT;
214 ctrl |= downlink->format & PDMOUTFORMAT;
215
216 /* Downlink channels */
217 mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
218
219 omap_mcpdm_write(MCPDM_CTRL, ctrl);
220
221 return 0;
222}
223
224/*
225 * Cleans McPDM uplink configuration.
226 * This function should be called when the stream is closed.
227 */
228int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink)
229{
230 int irq_mask = 0;
231
232 if (!uplink)
233 return -EINVAL;
234
235 /* Disable irq request generation */
236 irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
237 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
238
239 /* Disable DMA request generation */
240 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
241
242 /* Clear Downlink channels */
243 mcpdm->up_channels = 0;
244
245 mcpdm->uplink = NULL;
246
247 return 0;
248}
249
250/*
251 * Cleans McPDM downlink configuration.
252 * This function should be called when the stream is closed.
253 */
254int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink)
255{
256 int irq_mask = 0;
257
258 if (!downlink)
259 return -EINVAL;
260
261 /* Disable irq request generation */
262 irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
263 omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
264
265 /* Disable DMA request generation */
266 omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
267
268 /* clear Downlink channels */
269 mcpdm->dn_channels = 0;
270
271 mcpdm->downlink = NULL;
272
273 return 0;
274}
275
276static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
277{
278 struct omap_mcpdm *mcpdm_irq = dev_id;
279 int irq_status;
280
281 irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
282
283 /* Acknowledge irq event */
284 omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
285
286 if (irq & MCPDM_DN_IRQ_FULL) {
287 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
288 omap_mcpdm_reset_playback(1);
289 omap_mcpdm_playback_open(mcpdm_irq->downlink);
290 omap_mcpdm_reset_playback(0);
291 }
292
293 if (irq & MCPDM_DN_IRQ_EMPTY) {
294 dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
295 omap_mcpdm_reset_playback(1);
296 omap_mcpdm_playback_open(mcpdm_irq->downlink);
297 omap_mcpdm_reset_playback(0);
298 }
299
300 if (irq & MCPDM_DN_IRQ) {
301 dev_dbg(mcpdm_irq->dev, "DN write request\n");
302 }
303
304 if (irq & MCPDM_UP_IRQ_FULL) {
305 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
306 omap_mcpdm_reset_capture(1);
307 omap_mcpdm_capture_open(mcpdm_irq->uplink);
308 omap_mcpdm_reset_capture(0);
309 }
310
311 if (irq & MCPDM_UP_IRQ_EMPTY) {
312 dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
313 omap_mcpdm_reset_capture(1);
314 omap_mcpdm_capture_open(mcpdm_irq->uplink);
315 omap_mcpdm_reset_capture(0);
316 }
317
318 if (irq & MCPDM_UP_IRQ) {
319 dev_dbg(mcpdm_irq->dev, "UP write request\n");
320 }
321
322 return IRQ_HANDLED;
323}
324
325 int omap_mcpdm_request(void)
326{
327 int ret;
328
329 pm_runtime_get_sync(mcpdm->dev);
330
331 spin_lock(&mcpdm->lock);
332
333 if (!mcpdm->free) {
334 dev_err(mcpdm->dev, "McPDM interface is in use\n");
335 spin_unlock(&mcpdm->lock);
336 ret = -EBUSY;
337 goto err;
338 }
339 mcpdm->free = 0;
340
341 spin_unlock(&mcpdm->lock);
342
343 /* Disable lines while request is ongoing */
344 omap_mcpdm_write(MCPDM_CTRL, 0x00);
345
346 ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
347 0, "McPDM", (void *)mcpdm);
348 if (ret) {
349 dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
350 goto err;
351 }
352
353 return 0;
354
355err:
356 mcpdm->free = 1;
357 pm_runtime_put_sync(mcpdm->dev);
358 return ret;
359}
360
361void omap_mcpdm_free(void)
362{
363 spin_lock(&mcpdm->lock);
364 if (mcpdm->free) {
365 dev_err(mcpdm->dev, "McPDM interface is already free\n");
366 spin_unlock(&mcpdm->lock);
367 return;
368 }
369 mcpdm->free = 1;
370 spin_unlock(&mcpdm->lock);
371
372 pm_runtime_put_sync(mcpdm->dev);
373
374 free_irq(mcpdm->irq, (void *)mcpdm);
375}
376
377/* Enable/disable DC offset cancelation for the analog
378 * headset path (PDM channels 1 and 2).
379 */
380int omap_mcpdm_set_offset(int offset1, int offset2)
381{
382 int offset;
383
384 if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
385 return -EINVAL;
386
387 offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
388
389 /* offset cancellation for channel 1 */
390 if (offset1)
391 offset |= DN_OFST_RX1_EN;
392 else
393 offset &= ~DN_OFST_RX1_EN;
394
395 /* offset cancellation for channel 2 */
396 if (offset2)
397 offset |= DN_OFST_RX2_EN;
398 else
399 offset &= ~DN_OFST_RX2_EN;
400
401 omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
402
403 return 0;
404}
405
406int __devinit omap_mcpdm_probe(struct platform_device *pdev)
407{
408 struct resource *res;
409 int ret = 0;
410
411 mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
412 if (!mcpdm) {
413 ret = -ENOMEM;
414 goto exit;
415 }
416
417 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
418 if (res == NULL) {
419 dev_err(&pdev->dev, "no resource\n");
420 goto err_resource;
421 }
422
423 spin_lock_init(&mcpdm->lock);
424 mcpdm->free = 1;
425
426 if (!request_mem_region(res->start, resource_size(res), "McPDM")) {
427 ret = -EBUSY;
428 goto err_resource;
429 }
430
431 mcpdm->io_base = ioremap(res->start, resource_size(res));
432 if (!mcpdm->io_base) {
433 ret = -ENOMEM;
434 goto err_remap;
435 }
436
437 mcpdm->irq = platform_get_irq(pdev, 0);
438
439 mcpdm->dev = &pdev->dev;
440 platform_set_drvdata(pdev, mcpdm);
441
442 pm_runtime_enable(mcpdm->dev);
443
444 return 0;
445
446err_remap:
447 release_mem_region(res->start, resource_size(res));
448err_resource:
449 kfree(mcpdm);
450exit:
451 return ret;
452}
453
454int __devexit omap_mcpdm_remove(struct platform_device *pdev)
455{
456 struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
457 struct resource *res;
458
459 platform_set_drvdata(pdev, NULL);
460
461 pm_runtime_disable(mcpdm_ptr->dev);
462
463 iounmap(mcpdm_ptr->io_base);
464 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
465 release_mem_region(res->start, resource_size(res));
466
467 mcpdm_ptr->free = 0;
468 mcpdm_ptr->dev = NULL;
469
470 kfree(mcpdm_ptr);
471
472 return 0;
473}
474
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
deleted file mode 100644
index b055ad1d3dc2..000000000000
--- a/sound/soc/omap/mcpdm.h
+++ /dev/null
@@ -1,152 +0,0 @@
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
113struct omap_mcpdm_link {
114 int irq_mask;
115 int threshold;
116 int format;
117 int channels;
118};
119
120struct omap_mcpdm_platform_data {
121 unsigned long phys_base;
122 u16 irq;
123};
124
125struct 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 omap_mcpdm_link *downlink;
135 struct omap_mcpdm_link *uplink;
136 struct completion irq_completion;
137
138 int dn_channels;
139 int up_channels;
140};
141
142extern void omap_mcpdm_start(int stream);
143extern void omap_mcpdm_stop(int stream);
144extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink);
145extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink);
146extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink);
147extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink);
148extern int omap_mcpdm_request(void);
149extern void omap_mcpdm_free(void);
150extern int omap_mcpdm_set_offset(int offset1, int offset2);
151int __devinit omap_mcpdm_probe(struct platform_device *pdev);
152int __devexit omap_mcpdm_remove(struct platform_device *pdev);
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index 0820b9ec4907..159a5e98d66a 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -1,11 +1,12 @@
1/* 1/*
2 * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port 2 * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port
3 * 3 *
4 * Copyright (C) 2009 Texas Instruments 4 * Copyright (C) 2009 - 2011 Texas Instruments
5 * 5 *
6 * Author: Misael Lopez Cruz <x0052729@ti.com> 6 * Author: Misael Lopez Cruz <misael.lopez@ti.com>
7 * Contact: Jorge Eduardo Candelaria <x0107209@ti.com> 7 * Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
8 * Margarita Olaya <magi.olaya@ti.com> 8 * Margarita Olaya <magi.olaya@ti.com>
9 * Peter Ujfalusi <peter.ujfalusi@ti.com>
9 * 10 *
10 * This program is free software; you can redistribute it and/or 11 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License 12 * modify it under the terms of the GNU General Public License
@@ -25,41 +26,39 @@
25 26
26#include <linux/init.h> 27#include <linux/init.h>
27#include <linux/module.h> 28#include <linux/module.h>
28#include <linux/device.h> 29#include <linux/platform_device.h>
30#include <linux/interrupt.h>
31#include <linux/err.h>
32#include <linux/io.h>
33#include <linux/irq.h>
34#include <linux/slab.h>
35#include <linux/pm_runtime.h>
36
29#include <sound/core.h> 37#include <sound/core.h>
30#include <sound/pcm.h> 38#include <sound/pcm.h>
31#include <sound/pcm_params.h> 39#include <sound/pcm_params.h>
32#include <sound/initval.h>
33#include <sound/soc.h> 40#include <sound/soc.h>
34 41
35#include <plat/dma.h> 42#include <plat/dma.h>
36#include <plat/mcbsp.h> 43#include <plat/omap_hwmod.h>
37#include "mcpdm.h" 44#include "omap-mcpdm.h"
38#include "omap-pcm.h" 45#include "omap-pcm.h"
39 46
40struct omap_mcpdm_data { 47struct omap_mcpdm {
41 struct omap_mcpdm_link *links; 48 struct device *dev;
42 int active; 49 unsigned long phys_base;
43}; 50 void __iomem *io_base;
51 int irq;
44 52
45static struct omap_mcpdm_link omap_mcpdm_links[] = { 53 struct mutex mutex;
46 /* downlink */ 54
47 { 55 /* channel data */
48 .irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL, 56 u32 dn_channels;
49 .threshold = 2, 57 u32 up_channels;
50 .format = PDMOUTFORMAT_LJUST,
51 },
52 /* uplink */
53 {
54 .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL,
55 .threshold = UP_THRES_MAX - 3,
56 .format = PDMOUTFORMAT_LJUST,
57 },
58};
59 58
60static struct omap_mcpdm_data mcpdm_data = { 59 /* McPDM FIFO thresholds */
61 .links = omap_mcpdm_links, 60 u32 dn_threshold;
62 .active = 0, 61 u32 up_threshold;
63}; 62};
64 63
65/* 64/*
@@ -71,75 +70,232 @@ static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
71 .dma_req = OMAP44XX_DMA_MCPDM_DL, 70 .dma_req = OMAP44XX_DMA_MCPDM_DL,
72 .data_type = OMAP_DMA_DATA_TYPE_S32, 71 .data_type = OMAP_DMA_DATA_TYPE_S32,
73 .sync_mode = OMAP_DMA_SYNC_PACKET, 72 .sync_mode = OMAP_DMA_SYNC_PACKET,
74 .packet_size = 16, 73 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_DN_DATA,
75 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA,
76 }, 74 },
77 { 75 {
78 .name = "Audio capture", 76 .name = "Audio capture",
79 .dma_req = OMAP44XX_DMA_MCPDM_UP, 77 .dma_req = OMAP44XX_DMA_MCPDM_UP,
80 .data_type = OMAP_DMA_DATA_TYPE_S32, 78 .data_type = OMAP_DMA_DATA_TYPE_S32,
81 .sync_mode = OMAP_DMA_SYNC_PACKET, 79 .sync_mode = OMAP_DMA_SYNC_PACKET,
82 .packet_size = 16, 80 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_UP_DATA,
83 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA,
84 }, 81 },
85}; 82};
86 83
87static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, 84static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
88 struct snd_soc_dai *dai)
89{ 85{
90 int err = 0; 86 __raw_writel(val, mcpdm->io_base + reg);
87}
91 88
92 if (!dai->active) 89static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg)
93 err = omap_mcpdm_request(); 90{
91 return __raw_readl(mcpdm->io_base + reg);
92}
94 93
95 return err; 94#ifdef DEBUG
95static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm)
96{
97 dev_dbg(mcpdm->dev, "***********************\n");
98 dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
99 omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW));
100 dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
101 omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS));
102 dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
103 omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET));
104 dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
105 omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR));
106 dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
107 omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN));
108 dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
109 omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET));
110 dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
111 omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR));
112 dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
113 omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN));
114 dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
115 omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL));
116 dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
117 omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA));
118 dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
119 omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA));
120 dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
121 omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN));
122 dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
123 omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP));
124 dev_dbg(mcpdm->dev, "***********************\n");
96} 125}
126#else
127static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {}
128#endif
97 129
98static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, 130/*
99 struct snd_soc_dai *dai) 131 * Enables the transfer through the PDM interface to/from the Phoenix
132 * codec by enabling the corresponding UP or DN channels.
133 */
134static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
135{
136 u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
137
138 ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
139 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
140
141 ctrl |= mcpdm->dn_channels | mcpdm->up_channels;
142 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
143
144 ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
145 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
146}
147
148/*
149 * Disables the transfer through the PDM interface to/from the Phoenix
150 * codec by disabling the corresponding UP or DN channels.
151 */
152static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm)
153{
154 u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
155
156 ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
157 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
158
159 ctrl &= ~(mcpdm->dn_channels | mcpdm->up_channels);
160 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
161
162 ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
163 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
164
165}
166
167/*
168 * Is the physical McPDM interface active.
169 */
170static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
171{
172 return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) &
173 (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK);
174}
175
176/*
177 * Configures McPDM uplink, and downlink for audio.
178 * This function should be called before omap_mcpdm_start.
179 */
180static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
181{
182 omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
183 MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
184 MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
185
186 omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, mcpdm->dn_threshold);
187 omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, mcpdm->up_threshold);
188
189 omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET,
190 MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE);
191}
192
193/*
194 * Cleans McPDM uplink, and downlink configuration.
195 * This function should be called when the stream is closed.
196 */
197static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm)
100{ 198{
101 if (!dai->active) 199 /* Disable irq request generation for downlink */
102 omap_mcpdm_free(); 200 omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
201 MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL);
202
203 /* Disable DMA request generation for downlink */
204 omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE);
205
206 /* Disable irq request generation for uplink */
207 omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
208 MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
209
210 /* Disable DMA request generation for uplink */
211 omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE);
212}
213
214static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
215{
216 struct omap_mcpdm *mcpdm = dev_id;
217 int irq_status;
218
219 irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS);
220
221 /* Acknowledge irq event */
222 omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status);
223
224 if (irq_status & MCPDM_DN_IRQ_FULL)
225 dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n");
226
227 if (irq_status & MCPDM_DN_IRQ_EMPTY)
228 dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n");
229
230 if (irq_status & MCPDM_DN_IRQ)
231 dev_dbg(mcpdm->dev, "DN (playback) write request\n");
232
233 if (irq_status & MCPDM_UP_IRQ_FULL)
234 dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n");
235
236 if (irq_status & MCPDM_UP_IRQ_EMPTY)
237 dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n");
238
239 if (irq_status & MCPDM_UP_IRQ)
240 dev_dbg(mcpdm->dev, "UP (capture) write request\n");
241
242 return IRQ_HANDLED;
103} 243}
104 244
105static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, 245static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
106 struct snd_soc_dai *dai) 246 struct snd_soc_dai *dai)
107{ 247{
108 struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); 248 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
109 int stream = substream->stream;
110 int err = 0;
111
112 switch (cmd) {
113 case SNDRV_PCM_TRIGGER_START:
114 case SNDRV_PCM_TRIGGER_RESUME:
115 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
116 if (!mcpdm_priv->active++)
117 omap_mcpdm_start(stream);
118 break;
119 249
120 case SNDRV_PCM_TRIGGER_STOP: 250 mutex_lock(&mcpdm->mutex);
121 case SNDRV_PCM_TRIGGER_SUSPEND: 251
122 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 252 if (!dai->active) {
123 if (!--mcpdm_priv->active) 253 pm_runtime_get_sync(mcpdm->dev);
124 omap_mcpdm_stop(stream); 254
125 break; 255 /* Enable watch dog for ES above ES 1.0 to avoid saturation */
126 default: 256 if (omap_rev() != OMAP4430_REV_ES1_0) {
127 err = -EINVAL; 257 u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
258
259 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL,
260 ctrl | MCPDM_WD_EN);
261 }
262 omap_mcpdm_open_streams(mcpdm);
128 } 263 }
129 264
130 return err; 265 mutex_unlock(&mcpdm->mutex);
266
267 return 0;
268}
269
270static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
271 struct snd_soc_dai *dai)
272{
273 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
274
275 mutex_lock(&mcpdm->mutex);
276
277 if (!dai->active) {
278 if (omap_mcpdm_active(mcpdm)) {
279 omap_mcpdm_stop(mcpdm);
280 omap_mcpdm_close_streams(mcpdm);
281 }
282
283 if (!omap_mcpdm_active(mcpdm))
284 pm_runtime_put_sync(mcpdm->dev);
285 }
286
287 mutex_unlock(&mcpdm->mutex);
131} 288}
132 289
133static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, 290static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
134 struct snd_pcm_hw_params *params, 291 struct snd_pcm_hw_params *params,
135 struct snd_soc_dai *dai) 292 struct snd_soc_dai *dai)
136{ 293{
137 struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); 294 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
138 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
139 struct omap_pcm_dma_data *dma_data;
140 int threshold;
141 int stream = substream->stream; 295 int stream = substream->stream;
142 int channels, err, link_mask = 0; 296 struct omap_pcm_dma_data *dma_data;
297 int channels;
298 int link_mask = 0;
143 299
144 channels = params_channels(params); 300 channels = params_channels(params);
145 switch (channels) { 301 switch (channels) {
@@ -164,59 +320,87 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
164 } 320 }
165 321
166 dma_data = &omap_mcpdm_dai_dma_params[stream]; 322 dma_data = &omap_mcpdm_dai_dma_params[stream];
167 threshold = mcpdm_links[stream].threshold;
168 323
324 /* Configure McPDM channels, and DMA packet size */
169 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 325 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
170 mcpdm_links[stream].channels = link_mask << 3; 326 mcpdm->dn_channels = link_mask << 3;
171 dma_data->packet_size = (DN_THRES_MAX - threshold) * channels; 327 dma_data->packet_size =
172 328 (MCPDM_DN_THRES_MAX - mcpdm->dn_threshold) * channels;
173 err = omap_mcpdm_playback_open(&mcpdm_links[stream]);
174 } else { 329 } else {
175 mcpdm_links[stream].channels = link_mask << 0; 330 mcpdm->up_channels = link_mask << 0;
176 dma_data->packet_size = threshold * channels; 331 dma_data->packet_size = mcpdm->up_threshold * channels;
177
178 err = omap_mcpdm_capture_open(&mcpdm_links[stream]);
179 } 332 }
180 333
181 snd_soc_dai_set_dma_data(dai, substream, dma_data); 334 snd_soc_dai_set_dma_data(dai, substream, dma_data);
182 return err; 335
336 return 0;
183} 337}
184 338
185static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream, 339static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
186 struct snd_soc_dai *dai) 340 struct snd_soc_dai *dai)
187{ 341{
188 struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); 342 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
189 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
190 int stream = substream->stream;
191 int err;
192 343
193 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 344 if (!omap_mcpdm_active(mcpdm)) {
194 err = omap_mcpdm_playback_close(&mcpdm_links[stream]); 345 omap_mcpdm_start(mcpdm);
195 else 346 omap_mcpdm_reg_dump(mcpdm);
196 err = omap_mcpdm_capture_close(&mcpdm_links[stream]); 347 }
197 348
198 return err; 349 return 0;
199} 350}
200 351
201static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { 352static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
202 .startup = omap_mcpdm_dai_startup, 353 .startup = omap_mcpdm_dai_startup,
203 .shutdown = omap_mcpdm_dai_shutdown, 354 .shutdown = omap_mcpdm_dai_shutdown,
204 .trigger = omap_mcpdm_dai_trigger,
205 .hw_params = omap_mcpdm_dai_hw_params, 355 .hw_params = omap_mcpdm_dai_hw_params,
206 .hw_free = omap_mcpdm_dai_hw_free, 356 .prepare = omap_mcpdm_prepare,
207}; 357};
208 358
209#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 359static int omap_mcpdm_probe(struct snd_soc_dai *dai)
210#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) 360{
361 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
362 int ret;
363
364 pm_runtime_enable(mcpdm->dev);
365
366 /* Disable lines while request is ongoing */
367 pm_runtime_get_sync(mcpdm->dev);
368 omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
369
370 ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
371 0, "McPDM", (void *)mcpdm);
211 372
212static int omap_mcpdm_dai_probe(struct snd_soc_dai *dai) 373 pm_runtime_put_sync(mcpdm->dev);
374
375 if (ret) {
376 dev_err(mcpdm->dev, "Request for IRQ failed\n");
377 pm_runtime_disable(mcpdm->dev);
378 }
379
380 /* Configure McPDM threshold values */
381 mcpdm->dn_threshold = 2;
382 mcpdm->up_threshold = MCPDM_UP_THRES_MAX - 3;
383 return ret;
384}
385
386static int omap_mcpdm_remove(struct snd_soc_dai *dai)
213{ 387{
214 snd_soc_dai_set_drvdata(dai, &mcpdm_data); 388 struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
389
390 free_irq(mcpdm->irq, (void *)mcpdm);
391 pm_runtime_disable(mcpdm->dev);
392
215 return 0; 393 return 0;
216} 394}
217 395
396#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
397#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
398
218static struct snd_soc_dai_driver omap_mcpdm_dai = { 399static struct snd_soc_dai_driver omap_mcpdm_dai = {
219 .probe = omap_mcpdm_dai_probe, 400 .probe = omap_mcpdm_probe,
401 .remove = omap_mcpdm_remove,
402 .probe_order = SND_SOC_COMP_ORDER_LATE,
403 .remove_order = SND_SOC_COMP_ORDER_EARLY,
220 .playback = { 404 .playback = {
221 .channels_min = 1, 405 .channels_min = 1,
222 .channels_max = 4, 406 .channels_max = 4,
@@ -234,32 +418,79 @@ static struct snd_soc_dai_driver omap_mcpdm_dai = {
234 418
235static __devinit int asoc_mcpdm_probe(struct platform_device *pdev) 419static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
236{ 420{
237 int ret; 421 struct omap_mcpdm *mcpdm;
422 struct resource *res;
423 int ret = 0;
424
425 mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
426 if (!mcpdm)
427 return -ENOMEM;
428
429 platform_set_drvdata(pdev, mcpdm);
430
431 mutex_init(&mcpdm->mutex);
432
433 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
434 if (res == NULL) {
435 dev_err(&pdev->dev, "no resource\n");
436 goto err_res;
437 }
438
439 if (!request_mem_region(res->start, resource_size(res), "McPDM")) {
440 ret = -EBUSY;
441 goto err_res;
442 }
443
444 mcpdm->io_base = ioremap(res->start, resource_size(res));
445 if (!mcpdm->io_base) {
446 ret = -ENOMEM;
447 goto err_iomap;
448 }
449
450 mcpdm->irq = platform_get_irq(pdev, 0);
451 if (mcpdm->irq < 0) {
452 ret = mcpdm->irq;
453 goto err_irq;
454 }
455
456 mcpdm->dev = &pdev->dev;
238 457
239 ret = omap_mcpdm_probe(pdev);
240 if (ret < 0)
241 return ret;
242 ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai); 458 ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
243 if (ret < 0) 459 if (!ret)
244 omap_mcpdm_remove(pdev); 460 return 0;
461
462err_irq:
463 iounmap(mcpdm->io_base);
464err_iomap:
465 release_mem_region(res->start, resource_size(res));
466err_res:
467 kfree(mcpdm);
245 return ret; 468 return ret;
246} 469}
247 470
248static int __devexit asoc_mcpdm_remove(struct platform_device *pdev) 471static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
249{ 472{
473 struct omap_mcpdm *mcpdm = platform_get_drvdata(pdev);
474 struct resource *res;
475
250 snd_soc_unregister_dai(&pdev->dev); 476 snd_soc_unregister_dai(&pdev->dev);
251 omap_mcpdm_remove(pdev); 477
478 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
479 iounmap(mcpdm->io_base);
480 release_mem_region(res->start, resource_size(res));
481
482 kfree(mcpdm);
252 return 0; 483 return 0;
253} 484}
254 485
255static struct platform_driver asoc_mcpdm_driver = { 486static struct platform_driver asoc_mcpdm_driver = {
256 .driver = { 487 .driver = {
257 .name = "omap-mcpdm", 488 .name = "omap-mcpdm",
258 .owner = THIS_MODULE, 489 .owner = THIS_MODULE,
259 }, 490 },
260 491
261 .probe = asoc_mcpdm_probe, 492 .probe = asoc_mcpdm_probe,
262 .remove = __devexit_p(asoc_mcpdm_remove), 493 .remove = __devexit_p(asoc_mcpdm_remove),
263}; 494};
264 495
265static int __init snd_omap_mcpdm_init(void) 496static int __init snd_omap_mcpdm_init(void)
@@ -274,6 +505,6 @@ static void __exit snd_omap_mcpdm_exit(void)
274} 505}
275module_exit(snd_omap_mcpdm_exit); 506module_exit(snd_omap_mcpdm_exit);
276 507
277MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); 508MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
278MODULE_DESCRIPTION("OMAP PDM SoC Interface"); 509MODULE_DESCRIPTION("OMAP PDM SoC Interface");
279MODULE_LICENSE("GPL"); 510MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h
new file mode 100644
index 000000000000..d23122afdb10
--- /dev/null
+++ b/sound/soc/omap/omap-mcpdm.h
@@ -0,0 +1,95 @@
1/*
2 * omap-mcpdm.h
3 *
4 * Copyright (C) 2009 - 2011 Texas Instruments
5 *
6 * Contact: Misael Lopez Cruz <misael.lopez@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#define MCPDM_REG_REVISION 0x00
28#define MCPDM_REG_SYSCONFIG 0x10
29#define MCPDM_REG_IRQSTATUS_RAW 0x24
30#define MCPDM_REG_IRQSTATUS 0x28
31#define MCPDM_REG_IRQENABLE_SET 0x2C
32#define MCPDM_REG_IRQENABLE_CLR 0x30
33#define MCPDM_REG_IRQWAKE_EN 0x34
34#define MCPDM_REG_DMAENABLE_SET 0x38
35#define MCPDM_REG_DMAENABLE_CLR 0x3C
36#define MCPDM_REG_DMAWAKEEN 0x40
37#define MCPDM_REG_CTRL 0x44
38#define MCPDM_REG_DN_DATA 0x48
39#define MCPDM_REG_UP_DATA 0x4C
40#define MCPDM_REG_FIFO_CTRL_DN 0x50
41#define MCPDM_REG_FIFO_CTRL_UP 0x54
42#define MCPDM_REG_DN_OFFSET 0x58
43
44/*
45 * MCPDM_IRQ bit fields
46 * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
47 */
48
49#define MCPDM_DN_IRQ (1 << 0)
50#define MCPDM_DN_IRQ_EMPTY (1 << 1)
51#define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2)
52#define MCPDM_DN_IRQ_FULL (1 << 3)
53
54#define MCPDM_UP_IRQ (1 << 8)
55#define MCPDM_UP_IRQ_EMPTY (1 << 9)
56#define MCPDM_UP_IRQ_ALMST_FULL (1 << 10)
57#define MCPDM_UP_IRQ_FULL (1 << 11)
58
59#define MCPDM_DOWNLINK_IRQ_MASK 0x00F
60#define MCPDM_UPLINK_IRQ_MASK 0xF00
61
62/*
63 * MCPDM_DMAENABLE bit fields
64 */
65
66#define MCPDM_DMA_DN_ENABLE (1 << 0)
67#define MCPDM_DMA_UP_ENABLE (1 << 1)
68
69/*
70 * MCPDM_CTRL bit fields
71 */
72
73#define MCPDM_PDM_UPLINK_EN(x) (1 << (x - 1)) /* ch1 is at bit 0 */
74#define MCPDM_PDM_DOWNLINK_EN(x) (1 << (x + 2)) /* ch1 is at bit 3 */
75#define MCPDM_PDMOUTFORMAT (1 << 8)
76#define MCPDM_CMD_INT (1 << 9)
77#define MCPDM_STATUS_INT (1 << 10)
78#define MCPDM_SW_UP_RST (1 << 11)
79#define MCPDM_SW_DN_RST (1 << 12)
80#define MCPDM_WD_EN (1 << 14)
81#define MCPDM_PDM_UP_MASK 0x7
82#define MCPDM_PDM_DN_MASK (0x1f << 3)
83
84
85#define MCPDM_PDMOUTFORMAT_LJUST (0 << 8)
86#define MCPDM_PDMOUTFORMAT_RJUST (1 << 8)
87
88/*
89 * MCPDM_FIFO_CTRL bit fields
90 */
91
92#define MCPDM_UP_THRES_MAX 0xF
93#define MCPDM_DN_THRES_MAX 0xF
94
95#endif /* End of __OMAP_MCPDM_H__ */
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index 32782b96c3e4..a95177447126 100644
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -32,7 +32,7 @@
32#include <plat/hardware.h> 32#include <plat/hardware.h>
33#include <plat/mux.h> 33#include <plat/mux.h>
34 34
35#include "mcpdm.h" 35#include "omap-mcpdm.h"
36#include "omap-pcm.h" 36#include "omap-pcm.h"
37#include "../codecs/twl6040.h" 37#include "../codecs/twl6040.h"
38 38