Root/
Source at commit f57f34369b976ec22b1840e02ad6b64ee16e7c82 created 11 years 9 days ago. By Peter Ujfalusi, ASoC: omap-mcpdm: Fix for full duplex audio use case | |
---|---|
1 | /* |
2 | * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port |
3 | * |
4 | * Copyright (C) 2009 - 2011 Texas Instruments |
5 | * |
6 | * Author: Misael Lopez Cruz <misael.lopez@ti.com> |
7 | * Contact: Jorge Eduardo Candelaria <x0107209@ti.com> |
8 | * Margarita Olaya <magi.olaya@ti.com> |
9 | * Peter Ujfalusi <peter.ujfalusi@ti.com> |
10 | * |
11 | * This program is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU General Public License |
13 | * version 2 as published by the Free Software Foundation. |
14 | * |
15 | * This program is distributed in the hope that it will be useful, but |
16 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | * General Public License for more details. |
19 | * |
20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
23 | * 02110-1301 USA |
24 | * |
25 | */ |
26 | |
27 | #include <linux/init.h> |
28 | #include <linux/module.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 | #include <linux/of_device.h> |
37 | |
38 | #include <sound/core.h> |
39 | #include <sound/pcm.h> |
40 | #include <sound/pcm_params.h> |
41 | #include <sound/soc.h> |
42 | |
43 | #include "omap-mcpdm.h" |
44 | #include "omap-pcm.h" |
45 | |
46 | #define OMAP44XX_MCPDM_L3_BASE 0x49032000 |
47 | |
48 | struct mcpdm_link_config { |
49 | u32 link_mask; /* channel mask for the direction */ |
50 | u32 threshold; /* FIFO threshold */ |
51 | }; |
52 | |
53 | struct omap_mcpdm { |
54 | struct device *dev; |
55 | unsigned long phys_base; |
56 | void __iomem *io_base; |
57 | int irq; |
58 | |
59 | struct mutex mutex; |
60 | |
61 | /* Playback/Capture configuration */ |
62 | struct mcpdm_link_config config[2]; |
63 | |
64 | /* McPDM dn offsets for rx1, and 2 channels */ |
65 | u32 dn_rx_offset; |
66 | |
67 | /* McPDM needs to be restarted due to runtime reconfiguration */ |
68 | bool restart; |
69 | }; |
70 | |
71 | /* |
72 | * Stream DMA parameters |
73 | */ |
74 | static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { |
75 | { |
76 | .name = "Audio playback", |
77 | }, |
78 | { |
79 | .name = "Audio capture", |
80 | }, |
81 | }; |
82 | |
83 | static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val) |
84 | { |
85 | __raw_writel(val, mcpdm->io_base + reg); |
86 | } |
87 | |
88 | static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg) |
89 | { |
90 | return __raw_readl(mcpdm->io_base + reg); |
91 | } |
92 | |
93 | #ifdef DEBUG |
94 | static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) |
95 | { |
96 | dev_dbg(mcpdm->dev, "***********************\n"); |
97 | dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", |
98 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW)); |
99 | dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", |
100 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS)); |
101 | dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", |
102 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET)); |
103 | dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", |
104 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR)); |
105 | dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", |
106 | omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN)); |
107 | dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", |
108 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET)); |
109 | dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", |
110 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR)); |
111 | dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", |
112 | omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN)); |
113 | dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", |
114 | omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL)); |
115 | dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", |
116 | omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA)); |
117 | dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", |
118 | omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA)); |
119 | dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", |
120 | omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN)); |
121 | dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", |
122 | omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP)); |
123 | dev_dbg(mcpdm->dev, "***********************\n"); |
124 | } |
125 | #else |
126 | static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {} |
127 | #endif |
128 | |
129 | /* |
130 | * Enables the transfer through the PDM interface to/from the Phoenix |
131 | * codec by enabling the corresponding UP or DN channels. |
132 | */ |
133 | static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) |
134 | { |
135 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); |
136 | u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask; |
137 | |
138 | ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); |
139 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); |
140 | |
141 | ctrl |= link_mask; |
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 | */ |
152 | static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) |
153 | { |
154 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); |
155 | u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK; |
156 | |
157 | ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); |
158 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); |
159 | |
160 | ctrl &= ~(link_mask); |
161 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); |
162 | |
163 | ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); |
164 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); |
165 | |
166 | } |
167 | |
168 | /* |
169 | * Is the physical McPDM interface active. |
170 | */ |
171 | static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm) |
172 | { |
173 | return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) & |
174 | (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK); |
175 | } |
176 | |
177 | /* |
178 | * Configures McPDM uplink, and downlink for audio. |
179 | * This function should be called before omap_mcpdm_start. |
180 | */ |
181 | static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm) |
182 | { |
183 | omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET, |
184 | MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL | |
185 | MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); |
186 | |
187 | /* Enable DN RX1/2 offset cancellation feature, if configured */ |
188 | if (mcpdm->dn_rx_offset) { |
189 | u32 dn_offset = mcpdm->dn_rx_offset; |
190 | |
191 | omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); |
192 | dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN); |
193 | omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); |
194 | } |
195 | |
196 | omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, |
197 | mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold); |
198 | omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, |
199 | mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].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) |
263 | { |
264 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
265 | |
266 | mutex_lock(&mcpdm->mutex); |
267 | |
268 | if (!dai->active) { |
269 | u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); |
270 | |
271 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN); |
272 | omap_mcpdm_open_streams(mcpdm); |
273 | } |
274 | mutex_unlock(&mcpdm->mutex); |
275 | |
276 | snd_soc_dai_set_dma_data(dai, substream, |
277 | &omap_mcpdm_dai_dma_params[substream->stream]); |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, |
283 | struct snd_soc_dai *dai) |
284 | { |
285 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
286 | |
287 | mutex_lock(&mcpdm->mutex); |
288 | |
289 | if (!dai->active) { |
290 | if (omap_mcpdm_active(mcpdm)) { |
291 | omap_mcpdm_stop(mcpdm); |
292 | omap_mcpdm_close_streams(mcpdm); |
293 | mcpdm->config[0].link_mask = 0; |
294 | mcpdm->config[1].link_mask = 0; |
295 | } |
296 | } |
297 | |
298 | mutex_unlock(&mcpdm->mutex); |
299 | } |
300 | |
301 | static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, |
302 | struct snd_pcm_hw_params *params, |
303 | struct snd_soc_dai *dai) |
304 | { |
305 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
306 | int stream = substream->stream; |
307 | struct omap_pcm_dma_data *dma_data; |
308 | u32 threshold; |
309 | int channels; |
310 | int link_mask = 0; |
311 | |
312 | channels = params_channels(params); |
313 | switch (channels) { |
314 | case 5: |
315 | if (stream == SNDRV_PCM_STREAM_CAPTURE) |
316 | /* up to 3 channels for capture */ |
317 | return -EINVAL; |
318 | link_mask |= 1 << 4; |
319 | case 4: |
320 | if (stream == SNDRV_PCM_STREAM_CAPTURE) |
321 | /* up to 3 channels for capture */ |
322 | return -EINVAL; |
323 | link_mask |= 1 << 3; |
324 | case 3: |
325 | link_mask |= 1 << 2; |
326 | case 2: |
327 | link_mask |= 1 << 1; |
328 | case 1: |
329 | link_mask |= 1 << 0; |
330 | break; |
331 | default: |
332 | /* unsupported number of channels */ |
333 | return -EINVAL; |
334 | } |
335 | |
336 | dma_data = snd_soc_dai_get_dma_data(dai, substream); |
337 | |
338 | threshold = mcpdm->config[stream].threshold; |
339 | /* Configure McPDM channels, and DMA packet size */ |
340 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) { |
341 | link_mask <<= 3; |
342 | |
343 | /* If capture is not running assume a stereo stream to come */ |
344 | if (!mcpdm->config[!stream].link_mask) |
345 | mcpdm->config[!stream].link_mask = 0x3; |
346 | |
347 | dma_data->packet_size = |
348 | (MCPDM_DN_THRES_MAX - threshold) * channels; |
349 | } else { |
350 | /* If playback is not running assume a stereo stream to come */ |
351 | if (!mcpdm->config[!stream].link_mask) |
352 | mcpdm->config[!stream].link_mask = (0x3 << 3); |
353 | |
354 | dma_data->packet_size = threshold * channels; |
355 | } |
356 | |
357 | /* Check if we need to restart McPDM with this stream */ |
358 | if (mcpdm->config[stream].link_mask && |
359 | mcpdm->config[stream].link_mask != link_mask) |
360 | mcpdm->restart = true; |
361 | |
362 | mcpdm->config[stream].link_mask = link_mask; |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, |
368 | struct snd_soc_dai *dai) |
369 | { |
370 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
371 | |
372 | if (!omap_mcpdm_active(mcpdm)) { |
373 | omap_mcpdm_start(mcpdm); |
374 | omap_mcpdm_reg_dump(mcpdm); |
375 | } else if (mcpdm->restart) { |
376 | omap_mcpdm_stop(mcpdm); |
377 | omap_mcpdm_start(mcpdm); |
378 | mcpdm->restart = false; |
379 | omap_mcpdm_reg_dump(mcpdm); |
380 | } |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { |
386 | .startup = omap_mcpdm_dai_startup, |
387 | .shutdown = omap_mcpdm_dai_shutdown, |
388 | .hw_params = omap_mcpdm_dai_hw_params, |
389 | .prepare = omap_mcpdm_prepare, |
390 | }; |
391 | |
392 | static int omap_mcpdm_probe(struct snd_soc_dai *dai) |
393 | { |
394 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
395 | int ret; |
396 | |
397 | pm_runtime_enable(mcpdm->dev); |
398 | |
399 | /* Disable lines while request is ongoing */ |
400 | pm_runtime_get_sync(mcpdm->dev); |
401 | omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00); |
402 | |
403 | ret = devm_request_irq(mcpdm->dev, mcpdm->irq, omap_mcpdm_irq_handler, |
404 | 0, "McPDM", (void *)mcpdm); |
405 | |
406 | pm_runtime_put_sync(mcpdm->dev); |
407 | |
408 | if (ret) { |
409 | dev_err(mcpdm->dev, "Request for IRQ failed\n"); |
410 | pm_runtime_disable(mcpdm->dev); |
411 | } |
412 | |
413 | /* Configure McPDM threshold values */ |
414 | mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2; |
415 | mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold = |
416 | MCPDM_UP_THRES_MAX - 3; |
417 | return ret; |
418 | } |
419 | |
420 | static int omap_mcpdm_remove(struct snd_soc_dai *dai) |
421 | { |
422 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); |
423 | |
424 | pm_runtime_disable(mcpdm->dev); |
425 | |
426 | return 0; |
427 | } |
428 | |
429 | #define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) |
430 | #define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE |
431 | |
432 | static struct snd_soc_dai_driver omap_mcpdm_dai = { |
433 | .probe = omap_mcpdm_probe, |
434 | .remove = omap_mcpdm_remove, |
435 | .probe_order = SND_SOC_COMP_ORDER_LATE, |
436 | .remove_order = SND_SOC_COMP_ORDER_EARLY, |
437 | .playback = { |
438 | .channels_min = 1, |
439 | .channels_max = 5, |
440 | .rates = OMAP_MCPDM_RATES, |
441 | .formats = OMAP_MCPDM_FORMATS, |
442 | .sig_bits = 24, |
443 | }, |
444 | .capture = { |
445 | .channels_min = 1, |
446 | .channels_max = 3, |
447 | .rates = OMAP_MCPDM_RATES, |
448 | .formats = OMAP_MCPDM_FORMATS, |
449 | .sig_bits = 24, |
450 | }, |
451 | .ops = &omap_mcpdm_dai_ops, |
452 | }; |
453 | |
454 | void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, |
455 | u8 rx1, u8 rx2) |
456 | { |
457 | struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
458 | |
459 | mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2); |
460 | } |
461 | EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets); |
462 | |
463 | static int asoc_mcpdm_probe(struct platform_device *pdev) |
464 | { |
465 | struct omap_mcpdm *mcpdm; |
466 | struct resource *res; |
467 | |
468 | mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL); |
469 | if (!mcpdm) |
470 | return -ENOMEM; |
471 | |
472 | platform_set_drvdata(pdev, mcpdm); |
473 | |
474 | mutex_init(&mcpdm->mutex); |
475 | |
476 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); |
477 | if (res == NULL) |
478 | return -ENOMEM; |
479 | |
480 | omap_mcpdm_dai_dma_params[0].port_addr = res->start + MCPDM_REG_DN_DATA; |
481 | omap_mcpdm_dai_dma_params[1].port_addr = res->start + MCPDM_REG_UP_DATA; |
482 | |
483 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "dn_link"); |
484 | if (!res) |
485 | return -ENODEV; |
486 | |
487 | omap_mcpdm_dai_dma_params[0].dma_req = res->start; |
488 | |
489 | res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "up_link"); |
490 | if (!res) |
491 | return -ENODEV; |
492 | |
493 | omap_mcpdm_dai_dma_params[1].dma_req = res->start; |
494 | |
495 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); |
496 | if (res == NULL) |
497 | return -ENOMEM; |
498 | |
499 | mcpdm->io_base = devm_request_and_ioremap(&pdev->dev, res); |
500 | if (!mcpdm->io_base) { |
501 | dev_err(&pdev->dev, "cannot remap\n"); |
502 | return -ENOMEM; |
503 | } |
504 | |
505 | mcpdm->irq = platform_get_irq(pdev, 0); |
506 | if (mcpdm->irq < 0) |
507 | return mcpdm->irq; |
508 | |
509 | mcpdm->dev = &pdev->dev; |
510 | |
511 | return snd_soc_register_dai(&pdev->dev, &omap_mcpdm_dai); |
512 | } |
513 | |
514 | static int asoc_mcpdm_remove(struct platform_device *pdev) |
515 | { |
516 | snd_soc_unregister_dai(&pdev->dev); |
517 | return 0; |
518 | } |
519 | |
520 | static const struct of_device_id omap_mcpdm_of_match[] = { |
521 | { .compatible = "ti,omap4-mcpdm", }, |
522 | { } |
523 | }; |
524 | MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match); |
525 | |
526 | static struct platform_driver asoc_mcpdm_driver = { |
527 | .driver = { |
528 | .name = "omap-mcpdm", |
529 | .owner = THIS_MODULE, |
530 | .of_match_table = omap_mcpdm_of_match, |
531 | }, |
532 | |
533 | .probe = asoc_mcpdm_probe, |
534 | .remove = asoc_mcpdm_remove, |
535 | }; |
536 | |
537 | module_platform_driver(asoc_mcpdm_driver); |
538 | |
539 | MODULE_ALIAS("platform:omap-mcpdm"); |
540 | MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>"); |
541 | MODULE_DESCRIPTION("OMAP PDM SoC Interface"); |
542 | MODULE_LICENSE("GPL"); |
543 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9