aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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