blob: b7f4f7e015f3f4ef4c0dc91f46e1e4573ce39259 [file] [log] [blame]
Misael Lopez Cruzdb72c2f2010-02-22 15:09:22 -06001/*
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
42struct omap_mcpdm_data {
43 struct omap_mcpdm_link *links;
44 int active;
45};
46
47static 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
62static struct omap_mcpdm_data mcpdm_data = {
63 .links = omap_mcpdm_links,
64 .active = 0,
65};
66
67/*
68 * Stream DMA parameters
69 */
70static 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
89static 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
102static 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
112static 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
142static 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
Daniel Mack5f712b22010-03-22 10:11:15 +0100153 snd_soc_dai_set_dma_data(cpu_dai, substream,
154 &omap_mcpdm_dai_dma_params[stream]);
Misael Lopez Cruzdb72c2f2010-02-22 15:09:22 -0600155
156 channels = params_channels(params);
157 switch (channels) {
158 case 4:
159 if (stream == SNDRV_PCM_STREAM_CAPTURE)
160 /* up to 2 channels for capture */
161 return -EINVAL;
162 link_mask |= 1 << 3;
163 case 3:
164 if (stream == SNDRV_PCM_STREAM_CAPTURE)
165 /* up to 2 channels for capture */
166 return -EINVAL;
167 link_mask |= 1 << 2;
168 case 2:
169 link_mask |= 1 << 1;
170 case 1:
171 link_mask |= 1 << 0;
172 break;
173 default:
174 /* unsupported number of channels */
175 return -EINVAL;
176 }
177
178 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
179 mcpdm_links[stream].channels = link_mask << 3;
180 err = omap_mcpdm_playback_open(&mcpdm_links[stream]);
181 } else {
182 mcpdm_links[stream].channels = link_mask << 0;
183 err = omap_mcpdm_capture_open(&mcpdm_links[stream]);
184 }
185
186 return err;
187}
188
189static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream,
190 struct snd_soc_dai *dai)
191{
192 struct snd_soc_pcm_runtime *rtd = substream->private_data;
193 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
194 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
195 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
196 int stream = substream->stream;
197 int err;
198
199 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
200 err = omap_mcpdm_playback_close(&mcpdm_links[stream]);
201 else
202 err = omap_mcpdm_capture_close(&mcpdm_links[stream]);
203
204 return err;
205}
206
207static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
208 .startup = omap_mcpdm_dai_startup,
209 .shutdown = omap_mcpdm_dai_shutdown,
210 .trigger = omap_mcpdm_dai_trigger,
211 .hw_params = omap_mcpdm_dai_hw_params,
212 .hw_free = omap_mcpdm_dai_hw_free,
213};
214
215#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
216#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
217
218struct snd_soc_dai omap_mcpdm_dai = {
219 .name = "omap-mcpdm",
220 .id = -1,
221 .playback = {
222 .channels_min = 1,
223 .channels_max = 4,
224 .rates = OMAP_MCPDM_RATES,
225 .formats = OMAP_MCPDM_FORMATS,
226 },
227 .capture = {
228 .channels_min = 1,
229 .channels_max = 2,
230 .rates = OMAP_MCPDM_RATES,
231 .formats = OMAP_MCPDM_FORMATS,
232 },
233 .ops = &omap_mcpdm_dai_ops,
234 .private_data = &mcpdm_data,
235};
236EXPORT_SYMBOL_GPL(omap_mcpdm_dai);
237
238static int __init snd_omap_mcpdm_init(void)
239{
240 return snd_soc_register_dai(&omap_mcpdm_dai);
241}
242module_init(snd_omap_mcpdm_init);
243
244static void __exit snd_omap_mcpdm_exit(void)
245{
246 snd_soc_unregister_dai(&omap_mcpdm_dai);
247}
248module_exit(snd_omap_mcpdm_exit);
249
250MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
251MODULE_DESCRIPTION("OMAP PDM SoC Interface");
252MODULE_LICENSE("GPL");