diff options
Diffstat (limited to 'sound/soc/ep93xx/ep93xx-pcm.c')
-rw-r--r-- | sound/soc/ep93xx/ep93xx-pcm.c | 137 |
1 files changed, 77 insertions, 60 deletions
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index a456e491155f..a07f99c9c375 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/dmaengine.h> | ||
19 | #include <linux/dma-mapping.h> | 20 | #include <linux/dma-mapping.h> |
20 | 21 | ||
21 | #include <sound/core.h> | 22 | #include <sound/core.h> |
@@ -53,43 +54,34 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { | |||
53 | 54 | ||
54 | struct ep93xx_runtime_data | 55 | struct ep93xx_runtime_data |
55 | { | 56 | { |
56 | struct ep93xx_dma_m2p_client cl; | ||
57 | struct ep93xx_pcm_dma_params *params; | ||
58 | int pointer_bytes; | 57 | int pointer_bytes; |
59 | struct tasklet_struct period_tasklet; | ||
60 | int periods; | 58 | int periods; |
61 | struct ep93xx_dma_buffer buf[32]; | 59 | int period_bytes; |
60 | struct dma_chan *dma_chan; | ||
61 | struct ep93xx_dma_data dma_data; | ||
62 | }; | 62 | }; |
63 | 63 | ||
64 | static void ep93xx_pcm_period_elapsed(unsigned long data) | 64 | static void ep93xx_pcm_dma_callback(void *data) |
65 | { | 65 | { |
66 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; | 66 | struct snd_pcm_substream *substream = data; |
67 | snd_pcm_period_elapsed(substream); | 67 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; |
68 | } | ||
69 | 68 | ||
70 | static void ep93xx_pcm_buffer_started(void *cookie, | 69 | rtd->pointer_bytes += rtd->period_bytes; |
71 | struct ep93xx_dma_buffer *buf) | 70 | rtd->pointer_bytes %= rtd->period_bytes * rtd->periods; |
72 | { | 71 | |
72 | snd_pcm_period_elapsed(substream); | ||
73 | } | 73 | } |
74 | 74 | ||
75 | static void ep93xx_pcm_buffer_finished(void *cookie, | 75 | static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) |
76 | struct ep93xx_dma_buffer *buf, | ||
77 | int bytes, int error) | ||
78 | { | 76 | { |
79 | struct snd_pcm_substream *substream = cookie; | 77 | struct ep93xx_dma_data *data = filter_param; |
80 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
81 | |||
82 | if (buf == rtd->buf + rtd->periods - 1) | ||
83 | rtd->pointer_bytes = 0; | ||
84 | else | ||
85 | rtd->pointer_bytes += buf->size; | ||
86 | 78 | ||
87 | if (!error) { | 79 | if (data->direction == ep93xx_dma_chan_direction(chan)) { |
88 | ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf); | 80 | chan->private = data; |
89 | tasklet_schedule(&rtd->period_tasklet); | 81 | return true; |
90 | } else { | ||
91 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
92 | } | 82 | } |
83 | |||
84 | return false; | ||
93 | } | 85 | } |
94 | 86 | ||
95 | static int ep93xx_pcm_open(struct snd_pcm_substream *substream) | 87 | static int ep93xx_pcm_open(struct snd_pcm_substream *substream) |
@@ -98,30 +90,38 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream) | |||
98 | struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai; | 90 | struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai; |
99 | struct ep93xx_pcm_dma_params *dma_params; | 91 | struct ep93xx_pcm_dma_params *dma_params; |
100 | struct ep93xx_runtime_data *rtd; | 92 | struct ep93xx_runtime_data *rtd; |
93 | dma_cap_mask_t mask; | ||
101 | int ret; | 94 | int ret; |
102 | 95 | ||
103 | dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); | 96 | ret = snd_pcm_hw_constraint_integer(substream->runtime, |
97 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
98 | if (ret < 0) | ||
99 | return ret; | ||
100 | |||
104 | snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); | 101 | snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); |
105 | 102 | ||
106 | rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); | 103 | rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); |
107 | if (!rtd) | 104 | if (!rtd) |
108 | return -ENOMEM; | 105 | return -ENOMEM; |
109 | 106 | ||
110 | memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet)); | 107 | dma_cap_zero(mask); |
111 | rtd->period_tasklet.func = ep93xx_pcm_period_elapsed; | 108 | dma_cap_set(DMA_SLAVE, mask); |
112 | rtd->period_tasklet.data = (unsigned long)substream; | 109 | dma_cap_set(DMA_CYCLIC, mask); |
113 | 110 | ||
114 | rtd->cl.name = dma_params->name; | 111 | dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); |
115 | rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR | | 112 | rtd->dma_data.port = dma_params->dma_port; |
116 | ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | 113 | rtd->dma_data.name = dma_params->name; |
117 | EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX); | 114 | |
118 | rtd->cl.cookie = substream; | 115 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
119 | rtd->cl.buffer_started = ep93xx_pcm_buffer_started; | 116 | rtd->dma_data.direction = DMA_TO_DEVICE; |
120 | rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished; | 117 | else |
121 | ret = ep93xx_dma_m2p_client_register(&rtd->cl); | 118 | rtd->dma_data.direction = DMA_FROM_DEVICE; |
122 | if (ret < 0) { | 119 | |
120 | rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter, | ||
121 | &rtd->dma_data); | ||
122 | if (!rtd->dma_chan) { | ||
123 | kfree(rtd); | 123 | kfree(rtd); |
124 | return ret; | 124 | return -EINVAL; |
125 | } | 125 | } |
126 | 126 | ||
127 | substream->runtime->private_data = rtd; | 127 | substream->runtime->private_data = rtd; |
@@ -132,31 +132,52 @@ static int ep93xx_pcm_close(struct snd_pcm_substream *substream) | |||
132 | { | 132 | { |
133 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | 133 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; |
134 | 134 | ||
135 | ep93xx_dma_m2p_client_unregister(&rtd->cl); | 135 | dma_release_channel(rtd->dma_chan); |
136 | kfree(rtd); | 136 | kfree(rtd); |
137 | return 0; | 137 | return 0; |
138 | } | 138 | } |
139 | 139 | ||
140 | static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream) | ||
141 | { | ||
142 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
143 | struct ep93xx_runtime_data *rtd = runtime->private_data; | ||
144 | struct dma_chan *chan = rtd->dma_chan; | ||
145 | struct dma_device *dma_dev = chan->device; | ||
146 | struct dma_async_tx_descriptor *desc; | ||
147 | |||
148 | rtd->pointer_bytes = 0; | ||
149 | desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr, | ||
150 | rtd->period_bytes * rtd->periods, | ||
151 | rtd->period_bytes, | ||
152 | rtd->dma_data.direction); | ||
153 | if (!desc) | ||
154 | return -EINVAL; | ||
155 | |||
156 | desc->callback = ep93xx_pcm_dma_callback; | ||
157 | desc->callback_param = substream; | ||
158 | |||
159 | dmaengine_submit(desc); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static void ep93xx_pcm_dma_flush(struct snd_pcm_substream *substream) | ||
164 | { | ||
165 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
166 | struct ep93xx_runtime_data *rtd = runtime->private_data; | ||
167 | |||
168 | dmaengine_terminate_all(rtd->dma_chan); | ||
169 | } | ||
170 | |||
140 | static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, | 171 | static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, |
141 | struct snd_pcm_hw_params *params) | 172 | struct snd_pcm_hw_params *params) |
142 | { | 173 | { |
143 | struct snd_pcm_runtime *runtime = substream->runtime; | 174 | struct snd_pcm_runtime *runtime = substream->runtime; |
144 | struct ep93xx_runtime_data *rtd = runtime->private_data; | 175 | struct ep93xx_runtime_data *rtd = runtime->private_data; |
145 | size_t totsize = params_buffer_bytes(params); | ||
146 | size_t period = params_period_bytes(params); | ||
147 | int i; | ||
148 | 176 | ||
149 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 177 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
150 | runtime->dma_bytes = totsize; | ||
151 | |||
152 | rtd->periods = (totsize + period - 1) / period; | ||
153 | for (i = 0; i < rtd->periods; i++) { | ||
154 | rtd->buf[i].bus_addr = runtime->dma_addr + (i * period); | ||
155 | rtd->buf[i].size = period; | ||
156 | if ((i + 1) * period > totsize) | ||
157 | rtd->buf[i].size = totsize - (i * period); | ||
158 | } | ||
159 | 178 | ||
179 | rtd->periods = params_periods(params); | ||
180 | rtd->period_bytes = params_period_bytes(params); | ||
160 | return 0; | 181 | return 0; |
161 | } | 182 | } |
162 | 183 | ||
@@ -168,24 +189,20 @@ static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
168 | 189 | ||
169 | static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 190 | static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
170 | { | 191 | { |
171 | struct ep93xx_runtime_data *rtd = substream->runtime->private_data; | ||
172 | int ret; | 192 | int ret; |
173 | int i; | ||
174 | 193 | ||
175 | ret = 0; | 194 | ret = 0; |
176 | switch (cmd) { | 195 | switch (cmd) { |
177 | case SNDRV_PCM_TRIGGER_START: | 196 | case SNDRV_PCM_TRIGGER_START: |
178 | case SNDRV_PCM_TRIGGER_RESUME: | 197 | case SNDRV_PCM_TRIGGER_RESUME: |
179 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 198 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
180 | rtd->pointer_bytes = 0; | 199 | ret = ep93xx_pcm_dma_submit(substream); |
181 | for (i = 0; i < rtd->periods; i++) | ||
182 | ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i); | ||
183 | break; | 200 | break; |
184 | 201 | ||
185 | case SNDRV_PCM_TRIGGER_STOP: | 202 | case SNDRV_PCM_TRIGGER_STOP: |
186 | case SNDRV_PCM_TRIGGER_SUSPEND: | 203 | case SNDRV_PCM_TRIGGER_SUSPEND: |
187 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 204 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
188 | ep93xx_dma_m2p_flush(&rtd->cl); | 205 | ep93xx_pcm_dma_flush(substream); |
189 | break; | 206 | break; |
190 | 207 | ||
191 | default: | 208 | default: |