]>
Commit | Line | Data |
---|---|---|
8380222e SH |
1 | /* |
2 | * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer | |
3 | * | |
4 | * Copyright 2009 Sascha Hauer <[email protected]> | |
5 | * | |
6 | * This code is based on code copyrighted by Freescale, | |
7 | * Liam Girdwood, Javier Martin and probably others. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. | |
13 | */ | |
14 | #include <linux/clk.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/dma-mapping.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/platform_device.h> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
bf974a0d | 23 | #include <linux/dmaengine.h> |
258aea76 | 24 | #include <linux/types.h> |
8380222e SH |
25 | |
26 | #include <sound/core.h> | |
27 | #include <sound/initval.h> | |
28 | #include <sound/pcm.h> | |
29 | #include <sound/pcm_params.h> | |
30 | #include <sound/soc.h> | |
c307e8e3 | 31 | #include <sound/dmaengine_pcm.h> |
8380222e | 32 | |
bf974a0d | 33 | #include <mach/dma.h> |
8380222e | 34 | |
4762fbab | 35 | #include "imx-pcm.h" |
8380222e | 36 | |
bf974a0d | 37 | static bool filter(struct dma_chan *chan, void *param) |
8380222e | 38 | { |
bf974a0d SH |
39 | if (!imx_dma_is_general_purpose(chan)) |
40 | return false; | |
8380222e | 41 | |
c307e8e3 | 42 | chan->private = param; |
671999cb | 43 | |
c307e8e3 | 44 | return true; |
4564d10f LPC |
45 | } |
46 | ||
47 | static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, | |
48 | struct snd_pcm_hw_params *params) | |
49 | { | |
50 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
c307e8e3 | 51 | struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); |
4564d10f LPC |
52 | struct imx_pcm_dma_params *dma_params; |
53 | struct dma_slave_config slave_config; | |
4564d10f LPC |
54 | int ret; |
55 | ||
56 | dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | |
8380222e | 57 | |
c307e8e3 LPC |
58 | ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); |
59 | if (ret) | |
60 | return ret; | |
8380222e | 61 | |
258aea76 VK |
62 | slave_config.device_fc = false; |
63 | ||
bf974a0d | 64 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
bf974a0d | 65 | slave_config.dst_addr = dma_params->dma_addr; |
6584cb88 | 66 | slave_config.dst_maxburst = dma_params->burstsize; |
bf974a0d | 67 | } else { |
bf974a0d | 68 | slave_config.src_addr = dma_params->dma_addr; |
6584cb88 | 69 | slave_config.src_maxburst = dma_params->burstsize; |
8380222e SH |
70 | } |
71 | ||
4564d10f | 72 | ret = dmaengine_slave_config(chan, &slave_config); |
bf974a0d SH |
73 | if (ret) |
74 | return ret; | |
8380222e | 75 | |
8380222e SH |
76 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
77 | ||
8380222e SH |
78 | return 0; |
79 | } | |
80 | ||
8380222e SH |
81 | static struct snd_pcm_hardware snd_imx_hardware = { |
82 | .info = SNDRV_PCM_INFO_INTERLEAVED | | |
83 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
84 | SNDRV_PCM_INFO_MMAP | | |
85 | SNDRV_PCM_INFO_MMAP_VALID | | |
86 | SNDRV_PCM_INFO_PAUSE | | |
87 | SNDRV_PCM_INFO_RESUME, | |
88 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
89 | .rate_min = 8000, | |
90 | .channels_min = 2, | |
91 | .channels_max = 2, | |
92 | .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, | |
93 | .period_bytes_min = 128, | |
bf974a0d | 94 | .period_bytes_max = 65535, /* Limited by SDMA engine */ |
8380222e SH |
95 | .periods_min = 2, |
96 | .periods_max = 255, | |
97 | .fifo_size = 0, | |
98 | }; | |
99 | ||
100 | static int snd_imx_open(struct snd_pcm_substream *substream) | |
101 | { | |
c307e8e3 LPC |
102 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
103 | struct imx_pcm_dma_params *dma_params; | |
104 | struct imx_dma_data *dma_data; | |
8380222e SH |
105 | int ret; |
106 | ||
c307e8e3 | 107 | snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); |
8380222e | 108 | |
c307e8e3 | 109 | dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
8380222e | 110 | |
c307e8e3 | 111 | dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); |
b46b373f SG |
112 | dma_data->peripheral_type = dma_params->shared_peripheral ? |
113 | IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI; | |
c307e8e3 LPC |
114 | dma_data->priority = DMA_PRIO_HIGH; |
115 | dma_data->dma_request = dma_params->dma; | |
116 | ||
117 | ret = snd_dmaengine_pcm_open(substream, filter, dma_data); | |
118 | if (ret) { | |
119 | kfree(dma_data); | |
120 | return 0; | |
4564d10f LPC |
121 | } |
122 | ||
c307e8e3 | 123 | snd_dmaengine_pcm_set_data(substream, dma_data); |
bf974a0d SH |
124 | |
125 | return 0; | |
126 | } | |
127 | ||
128 | static int snd_imx_close(struct snd_pcm_substream *substream) | |
129 | { | |
c307e8e3 | 130 | struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); |
bf974a0d | 131 | |
c307e8e3 LPC |
132 | snd_dmaengine_pcm_close(substream); |
133 | kfree(dma_data); | |
bf974a0d | 134 | |
8380222e SH |
135 | return 0; |
136 | } | |
137 | ||
138 | static struct snd_pcm_ops imx_pcm_ops = { | |
139 | .open = snd_imx_open, | |
bf974a0d | 140 | .close = snd_imx_close, |
8380222e SH |
141 | .ioctl = snd_pcm_lib_ioctl, |
142 | .hw_params = snd_imx_pcm_hw_params, | |
c307e8e3 LPC |
143 | .trigger = snd_dmaengine_pcm_trigger, |
144 | .pointer = snd_dmaengine_pcm_pointer, | |
8380222e SH |
145 | .mmap = snd_imx_pcm_mmap, |
146 | }; | |
147 | ||
f0fba2ad LG |
148 | static struct snd_soc_platform_driver imx_soc_platform_mx2 = { |
149 | .ops = &imx_pcm_ops, | |
8380222e SH |
150 | .pcm_new = imx_pcm_new, |
151 | .pcm_free = imx_pcm_free, | |
152 | }; | |
153 | ||
f0fba2ad | 154 | static int __devinit imx_soc_platform_probe(struct platform_device *pdev) |
8380222e | 155 | { |
f0fba2ad LG |
156 | return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); |
157 | } | |
158 | ||
159 | static int __devexit imx_soc_platform_remove(struct platform_device *pdev) | |
160 | { | |
161 | snd_soc_unregister_platform(&pdev->dev); | |
162 | return 0; | |
163 | } | |
164 | ||
165 | static struct platform_driver imx_pcm_driver = { | |
166 | .driver = { | |
167 | .name = "imx-pcm-audio", | |
168 | .owner = THIS_MODULE, | |
169 | }, | |
f0fba2ad LG |
170 | .probe = imx_soc_platform_probe, |
171 | .remove = __devexit_p(imx_soc_platform_remove), | |
172 | }; | |
173 | ||
7a24b2ba | 174 | module_platform_driver(imx_pcm_driver); |
96dcabb9 AP |
175 | MODULE_LICENSE("GPL"); |
176 | MODULE_ALIAS("platform:imx-pcm-audio"); |