Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/omap/omap-mcpdm.c
10817 views
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 <[email protected]>
7
* Contact: Jorge Eduardo Candelaria <[email protected]>
8
* Margarita Olaya <[email protected]>
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/dma.h>
36
#include <plat/mcbsp.h>
37
#include "mcpdm.h"
38
#include "omap-pcm.h"
39
40
struct omap_mcpdm_data {
41
struct omap_mcpdm_link *links;
42
int active;
43
};
44
45
static struct omap_mcpdm_link omap_mcpdm_links[] = {
46
/* downlink */
47
{
48
.irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL,
49
.threshold = 1,
50
.format = PDMOUTFORMAT_LJUST,
51
},
52
/* uplink */
53
{
54
.irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL,
55
.threshold = 1,
56
.format = PDMOUTFORMAT_LJUST,
57
},
58
};
59
60
static struct omap_mcpdm_data mcpdm_data = {
61
.links = omap_mcpdm_links,
62
.active = 0,
63
};
64
65
/*
66
* Stream DMA parameters
67
*/
68
static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
69
{
70
.name = "Audio playback",
71
.dma_req = OMAP44XX_DMA_MCPDM_DL,
72
.data_type = OMAP_DMA_DATA_TYPE_S32,
73
.sync_mode = OMAP_DMA_SYNC_PACKET,
74
.packet_size = 16,
75
.port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA,
76
},
77
{
78
.name = "Audio capture",
79
.dma_req = OMAP44XX_DMA_MCPDM_UP,
80
.data_type = OMAP_DMA_DATA_TYPE_S32,
81
.sync_mode = OMAP_DMA_SYNC_PACKET,
82
.packet_size = 16,
83
.port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA,
84
},
85
};
86
87
static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
88
struct snd_soc_dai *dai)
89
{
90
int err = 0;
91
92
if (!dai->active)
93
err = omap_mcpdm_request();
94
95
return err;
96
}
97
98
static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
99
struct snd_soc_dai *dai)
100
{
101
if (!dai->active)
102
omap_mcpdm_free();
103
}
104
105
static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
106
struct snd_soc_dai *dai)
107
{
108
struct omap_mcpdm_data *mcpdm_priv = 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
120
case SNDRV_PCM_TRIGGER_STOP:
121
case SNDRV_PCM_TRIGGER_SUSPEND:
122
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
123
if (!--mcpdm_priv->active)
124
omap_mcpdm_stop(stream);
125
break;
126
default:
127
err = -EINVAL;
128
}
129
130
return err;
131
}
132
133
static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
134
struct snd_pcm_hw_params *params,
135
struct snd_soc_dai *dai)
136
{
137
struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai);
138
struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
139
int stream = substream->stream;
140
int channels, err, link_mask = 0;
141
142
snd_soc_dai_set_dma_data(dai, substream,
143
&omap_mcpdm_dai_dma_params[stream]);
144
145
channels = params_channels(params);
146
switch (channels) {
147
case 4:
148
if (stream == SNDRV_PCM_STREAM_CAPTURE)
149
/* up to 2 channels for capture */
150
return -EINVAL;
151
link_mask |= 1 << 3;
152
case 3:
153
if (stream == SNDRV_PCM_STREAM_CAPTURE)
154
/* up to 2 channels for capture */
155
return -EINVAL;
156
link_mask |= 1 << 2;
157
case 2:
158
link_mask |= 1 << 1;
159
case 1:
160
link_mask |= 1 << 0;
161
break;
162
default:
163
/* unsupported number of channels */
164
return -EINVAL;
165
}
166
167
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
168
mcpdm_links[stream].channels = link_mask << 3;
169
err = omap_mcpdm_playback_open(&mcpdm_links[stream]);
170
} else {
171
mcpdm_links[stream].channels = link_mask << 0;
172
err = omap_mcpdm_capture_open(&mcpdm_links[stream]);
173
}
174
175
return err;
176
}
177
178
static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream,
179
struct snd_soc_dai *dai)
180
{
181
struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai);
182
struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
183
int stream = substream->stream;
184
int err;
185
186
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
187
err = omap_mcpdm_playback_close(&mcpdm_links[stream]);
188
else
189
err = omap_mcpdm_capture_close(&mcpdm_links[stream]);
190
191
return err;
192
}
193
194
static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
195
.startup = omap_mcpdm_dai_startup,
196
.shutdown = omap_mcpdm_dai_shutdown,
197
.trigger = omap_mcpdm_dai_trigger,
198
.hw_params = omap_mcpdm_dai_hw_params,
199
.hw_free = omap_mcpdm_dai_hw_free,
200
};
201
202
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
203
#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
204
205
static int omap_mcpdm_dai_probe(struct snd_soc_dai *dai)
206
{
207
snd_soc_dai_set_drvdata(dai, &mcpdm_data);
208
return 0;
209
}
210
211
static struct snd_soc_dai_driver omap_mcpdm_dai = {
212
.probe = omap_mcpdm_dai_probe,
213
.playback = {
214
.channels_min = 1,
215
.channels_max = 4,
216
.rates = OMAP_MCPDM_RATES,
217
.formats = OMAP_MCPDM_FORMATS,
218
},
219
.capture = {
220
.channels_min = 1,
221
.channels_max = 2,
222
.rates = OMAP_MCPDM_RATES,
223
.formats = OMAP_MCPDM_FORMATS,
224
},
225
.ops = &omap_mcpdm_dai_ops,
226
};
227
228
static __devinit int asoc_mcpdm_probe(struct platform_device *pdev)
229
{
230
int ret;
231
232
ret = omap_mcpdm_probe(pdev);
233
if (ret < 0)
234
return ret;
235
ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai);
236
if (ret < 0)
237
omap_mcpdm_remove(pdev);
238
return ret;
239
}
240
241
static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
242
{
243
snd_soc_unregister_dai(&pdev->dev);
244
omap_mcpdm_remove(pdev);
245
return 0;
246
}
247
248
static struct platform_driver asoc_mcpdm_driver = {
249
.driver = {
250
.name = "omap-mcpdm-dai",
251
.owner = THIS_MODULE,
252
},
253
254
.probe = asoc_mcpdm_probe,
255
.remove = __devexit_p(asoc_mcpdm_remove),
256
};
257
258
static int __init snd_omap_mcpdm_init(void)
259
{
260
return platform_driver_register(&asoc_mcpdm_driver);
261
}
262
module_init(snd_omap_mcpdm_init);
263
264
static void __exit snd_omap_mcpdm_exit(void)
265
{
266
platform_driver_unregister(&asoc_mcpdm_driver);
267
}
268
module_exit(snd_omap_mcpdm_exit);
269
270
MODULE_AUTHOR("Misael Lopez Cruz <[email protected]>");
271
MODULE_DESCRIPTION("OMAP PDM SoC Interface");
272
MODULE_LICENSE("GPL");
273
274