diff options
Diffstat (limited to 'sound/soc/omap/omap-mcpdm.c')
-rw-r--r-- | sound/soc/omap/omap-mcpdm.c | 481 |
1 files changed, 373 insertions, 108 deletions
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index bed09c27e44c..41d17067cc73 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,42 @@ | |||
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 | ||
40 | struct omap_mcpdm_data { | 47 | struct 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 | ||
45 | static 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 = 1, | 57 | u32 up_channels; |
50 | .format = PDMOUTFORMAT_LJUST, | 58 | |
51 | }, | 59 | /* McPDM FIFO thresholds */ |
52 | /* uplink */ | 60 | u32 dn_threshold; |
53 | { | 61 | u32 up_threshold; |
54 | .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL, | ||
55 | .threshold = 1, | ||
56 | .format = PDMOUTFORMAT_LJUST, | ||
57 | }, | ||
58 | }; | ||
59 | 62 | ||
60 | static struct omap_mcpdm_data mcpdm_data = { | 63 | /* McPDM dn offsets for rx1, and 2 channels */ |
61 | .links = omap_mcpdm_links, | 64 | u32 dn_rx_offset; |
62 | .active = 0, | ||
63 | }; | 65 | }; |
64 | 66 | ||
65 | /* | 67 | /* |
@@ -71,88 +73,259 @@ static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { | |||
71 | .dma_req = OMAP44XX_DMA_MCPDM_DL, | 73 | .dma_req = OMAP44XX_DMA_MCPDM_DL, |
72 | .data_type = OMAP_DMA_DATA_TYPE_S32, | 74 | .data_type = OMAP_DMA_DATA_TYPE_S32, |
73 | .sync_mode = OMAP_DMA_SYNC_PACKET, | 75 | .sync_mode = OMAP_DMA_SYNC_PACKET, |
74 | .packet_size = 16, | 76 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_DN_DATA, |
75 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA, | ||
76 | }, | 77 | }, |
77 | { | 78 | { |
78 | .name = "Audio capture", | 79 | .name = "Audio capture", |
79 | .dma_req = OMAP44XX_DMA_MCPDM_UP, | 80 | .dma_req = OMAP44XX_DMA_MCPDM_UP, |
80 | .data_type = OMAP_DMA_DATA_TYPE_S32, | 81 | .data_type = OMAP_DMA_DATA_TYPE_S32, |
81 | .sync_mode = OMAP_DMA_SYNC_PACKET, | 82 | .sync_mode = OMAP_DMA_SYNC_PACKET, |
82 | .packet_size = 16, | 83 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_REG_UP_DATA, |
83 | .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA, | ||
84 | }, | 84 | }, |
85 | }; | 85 | }; |
86 | 86 | ||
87 | static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, | 87 | static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val) |
88 | struct snd_soc_dai *dai) | ||
89 | { | 88 | { |
90 | int err = 0; | 89 | __raw_writel(val, mcpdm->io_base + reg); |
90 | } | ||
91 | 91 | ||
92 | if (!dai->active) | 92 | static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg) |
93 | err = omap_mcpdm_request(); | 93 | { |
94 | return __raw_readl(mcpdm->io_base + reg); | ||
95 | } | ||
94 | 96 | ||
95 | return err; | 97 | #ifdef DEBUG |
98 | static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) | ||
99 | { | ||
100 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
101 | dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", | ||
102 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW)); | ||
103 | dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", | ||
104 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS)); | ||
105 | dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", | ||
106 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET)); | ||
107 | dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", | ||
108 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR)); | ||
109 | dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", | ||
110 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN)); | ||
111 | dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", | ||
112 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET)); | ||
113 | dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", | ||
114 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR)); | ||
115 | dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", | ||
116 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN)); | ||
117 | dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", | ||
118 | omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL)); | ||
119 | dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", | ||
120 | omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA)); | ||
121 | dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", | ||
122 | omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA)); | ||
123 | dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", | ||
124 | omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN)); | ||
125 | dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", | ||
126 | omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP)); | ||
127 | dev_dbg(mcpdm->dev, "***********************\n"); | ||
96 | } | 128 | } |
129 | #else | ||
130 | static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {} | ||
131 | #endif | ||
97 | 132 | ||
98 | static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, | 133 | /* |
99 | struct snd_soc_dai *dai) | 134 | * Enables the transfer through the PDM interface to/from the Phoenix |
135 | * codec by enabling the corresponding UP or DN channels. | ||
136 | */ | ||
137 | static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) | ||
138 | { | ||
139 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); | ||
140 | |||
141 | ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); | ||
142 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
143 | |||
144 | ctrl |= mcpdm->dn_channels | mcpdm->up_channels; | ||
145 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
146 | |||
147 | ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); | ||
148 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Disables the transfer through the PDM interface to/from the Phoenix | ||
153 | * codec by disabling the corresponding UP or DN channels. | ||
154 | */ | ||
155 | static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) | ||
156 | { | ||
157 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); | ||
158 | |||
159 | ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); | ||
160 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
161 | |||
162 | ctrl &= ~(mcpdm->dn_channels | mcpdm->up_channels); | ||
163 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
164 | |||
165 | ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); | ||
166 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); | ||
167 | |||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Is the physical McPDM interface active. | ||
172 | */ | ||
173 | static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm) | ||
174 | { | ||
175 | return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) & | ||
176 | (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK); | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * Configures McPDM uplink, and downlink for audio. | ||
181 | * This function should be called before omap_mcpdm_start. | ||
182 | */ | ||
183 | static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm) | ||
184 | { | ||
185 | omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET, | ||
186 | MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL | | ||
187 | MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); | ||
188 | |||
189 | /* Enable DN RX1/2 offset cancellation feature, if configured */ | ||
190 | if (mcpdm->dn_rx_offset) { | ||
191 | u32 dn_offset = mcpdm->dn_rx_offset; | ||
192 | |||
193 | omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); | ||
194 | dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN); | ||
195 | omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); | ||
196 | } | ||
197 | |||
198 | omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, mcpdm->dn_threshold); | ||
199 | omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, mcpdm->up_threshold); | ||
200 | |||
201 | omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET, | ||
202 | MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE); | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Cleans McPDM uplink, and downlink configuration. | ||
207 | * This function should be called when the stream is closed. | ||
208 | */ | ||
209 | static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm) | ||
210 | { | ||
211 | /* Disable irq request generation for downlink */ | ||
212 | omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, | ||
213 | MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL); | ||
214 | |||
215 | /* Disable DMA request generation for downlink */ | ||
216 | omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE); | ||
217 | |||
218 | /* Disable irq request generation for uplink */ | ||
219 | omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, | ||
220 | MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); | ||
221 | |||
222 | /* Disable DMA request generation for uplink */ | ||
223 | omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE); | ||
224 | |||
225 | /* Disable RX1/2 offset cancellation */ | ||
226 | if (mcpdm->dn_rx_offset) | ||
227 | omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0); | ||
228 | } | ||
229 | |||
230 | static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) | ||
231 | { | ||
232 | struct omap_mcpdm *mcpdm = dev_id; | ||
233 | int irq_status; | ||
234 | |||
235 | irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS); | ||
236 | |||
237 | /* Acknowledge irq event */ | ||
238 | omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status); | ||
239 | |||
240 | if (irq_status & MCPDM_DN_IRQ_FULL) | ||
241 | dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n"); | ||
242 | |||
243 | if (irq_status & MCPDM_DN_IRQ_EMPTY) | ||
244 | dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n"); | ||
245 | |||
246 | if (irq_status & MCPDM_DN_IRQ) | ||
247 | dev_dbg(mcpdm->dev, "DN (playback) write request\n"); | ||
248 | |||
249 | if (irq_status & MCPDM_UP_IRQ_FULL) | ||
250 | dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n"); | ||
251 | |||
252 | if (irq_status & MCPDM_UP_IRQ_EMPTY) | ||
253 | dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n"); | ||
254 | |||
255 | if (irq_status & MCPDM_UP_IRQ) | ||
256 | dev_dbg(mcpdm->dev, "UP (capture) write request\n"); | ||
257 | |||
258 | return IRQ_HANDLED; | ||
259 | } | ||
260 | |||
261 | static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, | ||
262 | struct snd_soc_dai *dai) | ||
100 | { | 263 | { |
101 | if (!dai->active) | 264 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
102 | omap_mcpdm_free(); | 265 | |
266 | mutex_lock(&mcpdm->mutex); | ||
267 | |||
268 | if (!dai->active) { | ||
269 | pm_runtime_get_sync(mcpdm->dev); | ||
270 | |||
271 | /* Enable watch dog for ES above ES 1.0 to avoid saturation */ | ||
272 | if (omap_rev() != OMAP4430_REV_ES1_0) { | ||
273 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); | ||
274 | |||
275 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, | ||
276 | ctrl | MCPDM_WD_EN); | ||
277 | } | ||
278 | omap_mcpdm_open_streams(mcpdm); | ||
279 | } | ||
280 | |||
281 | mutex_unlock(&mcpdm->mutex); | ||
282 | |||
283 | return 0; | ||
103 | } | 284 | } |
104 | 285 | ||
105 | static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, | 286 | static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, |
106 | struct snd_soc_dai *dai) | 287 | struct snd_soc_dai *dai) |
107 | { | 288 | { |
108 | struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); | 289 | 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 | 290 | ||
120 | case SNDRV_PCM_TRIGGER_STOP: | 291 | mutex_lock(&mcpdm->mutex); |
121 | case SNDRV_PCM_TRIGGER_SUSPEND: | 292 | |
122 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 293 | if (!dai->active) { |
123 | if (!--mcpdm_priv->active) | 294 | if (omap_mcpdm_active(mcpdm)) { |
124 | omap_mcpdm_stop(stream); | 295 | omap_mcpdm_stop(mcpdm); |
125 | break; | 296 | omap_mcpdm_close_streams(mcpdm); |
126 | default: | 297 | } |
127 | err = -EINVAL; | 298 | |
299 | if (!omap_mcpdm_active(mcpdm)) | ||
300 | pm_runtime_put_sync(mcpdm->dev); | ||
128 | } | 301 | } |
129 | 302 | ||
130 | return err; | 303 | mutex_unlock(&mcpdm->mutex); |
131 | } | 304 | } |
132 | 305 | ||
133 | static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, | 306 | static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, |
134 | struct snd_pcm_hw_params *params, | 307 | struct snd_pcm_hw_params *params, |
135 | struct snd_soc_dai *dai) | 308 | struct snd_soc_dai *dai) |
136 | { | 309 | { |
137 | struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); | 310 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
138 | struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; | ||
139 | int stream = substream->stream; | 311 | int stream = substream->stream; |
140 | int channels, err, link_mask = 0; | 312 | struct omap_pcm_dma_data *dma_data; |
141 | 313 | int channels; | |
142 | snd_soc_dai_set_dma_data(dai, substream, | 314 | int link_mask = 0; |
143 | &omap_mcpdm_dai_dma_params[stream]); | ||
144 | 315 | ||
145 | channels = params_channels(params); | 316 | channels = params_channels(params); |
146 | switch (channels) { | 317 | switch (channels) { |
318 | case 5: | ||
319 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
320 | /* up to 3 channels for capture */ | ||
321 | return -EINVAL; | ||
322 | link_mask |= 1 << 4; | ||
147 | case 4: | 323 | case 4: |
148 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | 324 | if (stream == SNDRV_PCM_STREAM_CAPTURE) |
149 | /* up to 2 channels for capture */ | 325 | /* up to 3 channels for capture */ |
150 | return -EINVAL; | 326 | return -EINVAL; |
151 | link_mask |= 1 << 3; | 327 | link_mask |= 1 << 3; |
152 | case 3: | 328 | case 3: |
153 | if (stream == SNDRV_PCM_STREAM_CAPTURE) | ||
154 | /* up to 2 channels for capture */ | ||
155 | return -EINVAL; | ||
156 | link_mask |= 1 << 2; | 329 | link_mask |= 1 << 2; |
157 | case 2: | 330 | case 2: |
158 | link_mask |= 1 << 1; | 331 | link_mask |= 1 << 1; |
@@ -164,95 +337,187 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, | |||
164 | return -EINVAL; | 337 | return -EINVAL; |
165 | } | 338 | } |
166 | 339 | ||
340 | dma_data = &omap_mcpdm_dai_dma_params[stream]; | ||
341 | |||
342 | /* Configure McPDM channels, and DMA packet size */ | ||
167 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { | 343 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
168 | mcpdm_links[stream].channels = link_mask << 3; | 344 | mcpdm->dn_channels = link_mask << 3; |
169 | err = omap_mcpdm_playback_open(&mcpdm_links[stream]); | 345 | dma_data->packet_size = |
346 | (MCPDM_DN_THRES_MAX - mcpdm->dn_threshold) * channels; | ||
170 | } else { | 347 | } else { |
171 | mcpdm_links[stream].channels = link_mask << 0; | 348 | mcpdm->up_channels = link_mask << 0; |
172 | err = omap_mcpdm_capture_open(&mcpdm_links[stream]); | 349 | dma_data->packet_size = mcpdm->up_threshold * channels; |
173 | } | 350 | } |
174 | 351 | ||
175 | return err; | 352 | snd_soc_dai_set_dma_data(dai, substream, dma_data); |
353 | |||
354 | return 0; | ||
176 | } | 355 | } |
177 | 356 | ||
178 | static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream, | 357 | static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, |
179 | struct snd_soc_dai *dai) | 358 | struct snd_soc_dai *dai) |
180 | { | 359 | { |
181 | struct omap_mcpdm_data *mcpdm_priv = snd_soc_dai_get_drvdata(dai); | 360 | struct omap_mcpdm *mcpdm = 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 | 361 | ||
186 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 362 | if (!omap_mcpdm_active(mcpdm)) { |
187 | err = omap_mcpdm_playback_close(&mcpdm_links[stream]); | 363 | omap_mcpdm_start(mcpdm); |
188 | else | 364 | omap_mcpdm_reg_dump(mcpdm); |
189 | err = omap_mcpdm_capture_close(&mcpdm_links[stream]); | 365 | } |
190 | 366 | ||
191 | return err; | 367 | return 0; |
192 | } | 368 | } |
193 | 369 | ||
194 | static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { | 370 | static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { |
195 | .startup = omap_mcpdm_dai_startup, | 371 | .startup = omap_mcpdm_dai_startup, |
196 | .shutdown = omap_mcpdm_dai_shutdown, | 372 | .shutdown = omap_mcpdm_dai_shutdown, |
197 | .trigger = omap_mcpdm_dai_trigger, | ||
198 | .hw_params = omap_mcpdm_dai_hw_params, | 373 | .hw_params = omap_mcpdm_dai_hw_params, |
199 | .hw_free = omap_mcpdm_dai_hw_free, | 374 | .prepare = omap_mcpdm_prepare, |
200 | }; | 375 | }; |
201 | 376 | ||
202 | #define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | 377 | static int omap_mcpdm_probe(struct snd_soc_dai *dai) |
203 | #define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) | 378 | { |
379 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); | ||
380 | int ret; | ||
204 | 381 | ||
205 | static int omap_mcpdm_dai_probe(struct snd_soc_dai *dai) | 382 | pm_runtime_enable(mcpdm->dev); |
383 | |||
384 | /* Disable lines while request is ongoing */ | ||
385 | pm_runtime_get_sync(mcpdm->dev); | ||
386 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00); | ||
387 | |||
388 | ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, | ||
389 | 0, "McPDM", (void *)mcpdm); | ||
390 | |||
391 | pm_runtime_put_sync(mcpdm->dev); | ||
392 | |||
393 | if (ret) { | ||
394 | dev_err(mcpdm->dev, "Request for IRQ failed\n"); | ||
395 | pm_runtime_disable(mcpdm->dev); | ||
396 | } | ||
397 | |||
398 | /* Configure McPDM threshold values */ | ||
399 | mcpdm->dn_threshold = 2; | ||
400 | mcpdm->up_threshold = MCPDM_UP_THRES_MAX - 3; | ||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | static int omap_mcpdm_remove(struct snd_soc_dai *dai) | ||
206 | { | 405 | { |
207 | snd_soc_dai_set_drvdata(dai, &mcpdm_data); | 406 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
407 | |||
408 | free_irq(mcpdm->irq, (void *)mcpdm); | ||
409 | pm_runtime_disable(mcpdm->dev); | ||
410 | |||
208 | return 0; | 411 | return 0; |
209 | } | 412 | } |
210 | 413 | ||
414 | #define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
415 | #define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE | ||
416 | |||
211 | static struct snd_soc_dai_driver omap_mcpdm_dai = { | 417 | static struct snd_soc_dai_driver omap_mcpdm_dai = { |
212 | .probe = omap_mcpdm_dai_probe, | 418 | .probe = omap_mcpdm_probe, |
419 | .remove = omap_mcpdm_remove, | ||
420 | .probe_order = SND_SOC_COMP_ORDER_LATE, | ||
421 | .remove_order = SND_SOC_COMP_ORDER_EARLY, | ||
213 | .playback = { | 422 | .playback = { |
214 | .channels_min = 1, | 423 | .channels_min = 1, |
215 | .channels_max = 4, | 424 | .channels_max = 5, |
216 | .rates = OMAP_MCPDM_RATES, | 425 | .rates = OMAP_MCPDM_RATES, |
217 | .formats = OMAP_MCPDM_FORMATS, | 426 | .formats = OMAP_MCPDM_FORMATS, |
218 | }, | 427 | }, |
219 | .capture = { | 428 | .capture = { |
220 | .channels_min = 1, | 429 | .channels_min = 1, |
221 | .channels_max = 2, | 430 | .channels_max = 3, |
222 | .rates = OMAP_MCPDM_RATES, | 431 | .rates = OMAP_MCPDM_RATES, |
223 | .formats = OMAP_MCPDM_FORMATS, | 432 | .formats = OMAP_MCPDM_FORMATS, |
224 | }, | 433 | }, |
225 | .ops = &omap_mcpdm_dai_ops, | 434 | .ops = &omap_mcpdm_dai_ops, |
226 | }; | 435 | }; |
227 | 436 | ||
437 | void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, | ||
438 | u8 rx1, u8 rx2) | ||
439 | { | ||
440 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai); | ||
441 | |||
442 | mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2); | ||
443 | } | ||
444 | EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets); | ||
445 | |||
228 | static __devinit int asoc_mcpdm_probe(struct platform_device *pdev) | 446 | static __devinit int asoc_mcpdm_probe(struct platform_device *pdev) |
229 | { | 447 | { |
230 | int ret; | 448 | struct omap_mcpdm *mcpdm; |
449 | struct resource *res; | ||
450 | int ret = 0; | ||
451 | |||
452 | mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL); | ||
453 | if (!mcpdm) | ||
454 | return -ENOMEM; | ||
455 | |||
456 | platform_set_drvdata(pdev, mcpdm); | ||
457 | |||
458 | mutex_init(&mcpdm->mutex); | ||
459 | |||
460 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
461 | if (res == NULL) { | ||
462 | dev_err(&pdev->dev, "no resource\n"); | ||
463 | goto err_res; | ||
464 | } | ||
465 | |||
466 | if (!request_mem_region(res->start, resource_size(res), "McPDM")) { | ||
467 | ret = -EBUSY; | ||
468 | goto err_res; | ||
469 | } | ||
470 | |||
471 | mcpdm->io_base = ioremap(res->start, resource_size(res)); | ||
472 | if (!mcpdm->io_base) { | ||
473 | ret = -ENOMEM; | ||
474 | goto err_iomap; | ||
475 | } | ||
476 | |||
477 | mcpdm->irq = platform_get_irq(pdev, 0); | ||
478 | if (mcpdm->irq < 0) { | ||
479 | ret = mcpdm->irq; | ||
480 | goto err_irq; | ||
481 | } | ||
482 | |||
483 | mcpdm->dev = &pdev->dev; | ||
231 | 484 | ||
232 | ret = omap_mcpdm_probe(pdev); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai); | 485 | ret = snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai); |
236 | if (ret < 0) | 486 | if (!ret) |
237 | omap_mcpdm_remove(pdev); | 487 | return 0; |
488 | |||
489 | err_irq: | ||
490 | iounmap(mcpdm->io_base); | ||
491 | err_iomap: | ||
492 | release_mem_region(res->start, resource_size(res)); | ||
493 | err_res: | ||
494 | kfree(mcpdm); | ||
238 | return ret; | 495 | return ret; |
239 | } | 496 | } |
240 | 497 | ||
241 | static int __devexit asoc_mcpdm_remove(struct platform_device *pdev) | 498 | static int __devexit asoc_mcpdm_remove(struct platform_device *pdev) |
242 | { | 499 | { |
500 | struct omap_mcpdm *mcpdm = platform_get_drvdata(pdev); | ||
501 | struct resource *res; | ||
502 | |||
243 | snd_soc_unregister_dai(&pdev->dev); | 503 | snd_soc_unregister_dai(&pdev->dev); |
244 | omap_mcpdm_remove(pdev); | 504 | |
505 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
506 | iounmap(mcpdm->io_base); | ||
507 | release_mem_region(res->start, resource_size(res)); | ||
508 | |||
509 | kfree(mcpdm); | ||
245 | return 0; | 510 | return 0; |
246 | } | 511 | } |
247 | 512 | ||
248 | static struct platform_driver asoc_mcpdm_driver = { | 513 | static struct platform_driver asoc_mcpdm_driver = { |
249 | .driver = { | 514 | .driver = { |
250 | .name = "omap-mcpdm-dai", | 515 | .name = "omap-mcpdm", |
251 | .owner = THIS_MODULE, | 516 | .owner = THIS_MODULE, |
252 | }, | 517 | }, |
253 | 518 | ||
254 | .probe = asoc_mcpdm_probe, | 519 | .probe = asoc_mcpdm_probe, |
255 | .remove = __devexit_p(asoc_mcpdm_remove), | 520 | .remove = __devexit_p(asoc_mcpdm_remove), |
256 | }; | 521 | }; |
257 | 522 | ||
258 | static int __init snd_omap_mcpdm_init(void) | 523 | static int __init snd_omap_mcpdm_init(void) |
@@ -267,6 +532,6 @@ static void __exit snd_omap_mcpdm_exit(void) | |||
267 | } | 532 | } |
268 | module_exit(snd_omap_mcpdm_exit); | 533 | module_exit(snd_omap_mcpdm_exit); |
269 | 534 | ||
270 | MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); | 535 | MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); |
271 | MODULE_DESCRIPTION("OMAP PDM SoC Interface"); | 536 | MODULE_DESCRIPTION("OMAP PDM SoC Interface"); |
272 | MODULE_LICENSE("GPL"); | 537 | MODULE_LICENSE("GPL"); |