aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/ep93xx/ep93xx-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/ep93xx/ep93xx-pcm.c')
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.c137
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
54struct ep93xx_runtime_data 55struct 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
64static void ep93xx_pcm_period_elapsed(unsigned long data) 64static 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
70static 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
75static void ep93xx_pcm_buffer_finished(void *cookie, 75static 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
95static int ep93xx_pcm_open(struct snd_pcm_substream *substream) 87static 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
140static 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
163static 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
140static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, 171static 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
169static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 190static 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: