diff options
author | Boojin Kim <boojin.kim@samsung.com> | 2011-09-01 20:44:43 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2011-09-14 01:40:04 -0400 |
commit | 344b4c48887a443f7478fc7047d1397b20821ed3 (patch) | |
tree | a3fdf36f35b43d41f1402f2e6939fe761f575be8 /sound/soc/samsung | |
parent | 82ab8cd7ec32194757ac73a66633be73ba88ea69 (diff) |
ASoC: Samsung: Update DMA interface
This patch adds to support the DMA PL330 driver that uses
DMA generic API. Samsung sound driver uses DMA generic API
if architecture supports it. Otherwise, use samsung specific
S3C-PL330 API driver to transfer PCM data.
Signed-off-by: Boojin Kim <boojin.kim@samsung.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Vinod Koul <vinod.koul@intel.com>
Cc: Jassi Brar <jassisinghbrar@gmail.com>
Cc: Liam Girdwood <lrg@ti.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
[kgene.kim@samsung.com: removed useless variable]
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'sound/soc/samsung')
-rw-r--r-- | sound/soc/samsung/ac97.c | 10 | ||||
-rw-r--r-- | sound/soc/samsung/dma.c | 146 | ||||
-rw-r--r-- | sound/soc/samsung/dma.h | 4 |
3 files changed, 71 insertions, 89 deletions
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index f97110e72e85..b4f9b0003685 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c | |||
@@ -271,7 +271,10 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, | |||
271 | 271 | ||
272 | writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); | 272 | writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); |
273 | 273 | ||
274 | s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); | 274 | if (!dma_data->ops) |
275 | dma_data->ops = samsung_dma_get_ops(); | ||
276 | |||
277 | dma_data->ops->started(dma_data->channel); | ||
275 | 278 | ||
276 | return 0; | 279 | return 0; |
277 | } | 280 | } |
@@ -317,7 +320,10 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, | |||
317 | 320 | ||
318 | writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); | 321 | writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); |
319 | 322 | ||
320 | s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); | 323 | if (!dma_data->ops) |
324 | dma_data->ops = samsung_dma_get_ops(); | ||
325 | |||
326 | dma_data->ops->started(dma_data->channel); | ||
321 | 327 | ||
322 | return 0; | 328 | return 0; |
323 | } | 329 | } |
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 9465588b02f2..851346f7d68d 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c | |||
@@ -54,7 +54,6 @@ struct runtime_data { | |||
54 | spinlock_t lock; | 54 | spinlock_t lock; |
55 | int state; | 55 | int state; |
56 | unsigned int dma_loaded; | 56 | unsigned int dma_loaded; |
57 | unsigned int dma_limit; | ||
58 | unsigned int dma_period; | 57 | unsigned int dma_period; |
59 | dma_addr_t dma_start; | 58 | dma_addr_t dma_start; |
60 | dma_addr_t dma_pos; | 59 | dma_addr_t dma_pos; |
@@ -62,77 +61,79 @@ struct runtime_data { | |||
62 | struct s3c_dma_params *params; | 61 | struct s3c_dma_params *params; |
63 | }; | 62 | }; |
64 | 63 | ||
64 | static void audio_buffdone(void *data); | ||
65 | |||
65 | /* dma_enqueue | 66 | /* dma_enqueue |
66 | * | 67 | * |
67 | * place a dma buffer onto the queue for the dma system | 68 | * place a dma buffer onto the queue for the dma system |
68 | * to handle. | 69 | * to handle. |
69 | */ | 70 | */ |
70 | static void dma_enqueue(struct snd_pcm_substream *substream) | 71 | static void dma_enqueue(struct snd_pcm_substream *substream) |
71 | { | 72 | { |
72 | struct runtime_data *prtd = substream->runtime->private_data; | 73 | struct runtime_data *prtd = substream->runtime->private_data; |
73 | dma_addr_t pos = prtd->dma_pos; | 74 | dma_addr_t pos = prtd->dma_pos; |
74 | unsigned int limit; | 75 | unsigned int limit; |
75 | int ret; | 76 | struct samsung_dma_prep_info dma_info; |
76 | 77 | ||
77 | pr_debug("Entered %s\n", __func__); | 78 | pr_debug("Entered %s\n", __func__); |
78 | 79 | ||
79 | if (s3c_dma_has_circular()) | 80 | limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; |
80 | limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; | ||
81 | else | ||
82 | limit = prtd->dma_limit; | ||
83 | 81 | ||
84 | pr_debug("%s: loaded %d, limit %d\n", | 82 | pr_debug("%s: loaded %d, limit %d\n", |
85 | __func__, prtd->dma_loaded, limit); | 83 | __func__, prtd->dma_loaded, limit); |
86 | 84 | ||
87 | while (prtd->dma_loaded < limit) { | 85 | dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE); |
88 | unsigned long len = prtd->dma_period; | 86 | dma_info.direction = |
87 | (substream->stream == SNDRV_PCM_STREAM_PLAYBACK | ||
88 | ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | ||
89 | dma_info.fp = audio_buffdone; | ||
90 | dma_info.fp_param = substream; | ||
91 | dma_info.period = prtd->dma_period; | ||
92 | dma_info.len = prtd->dma_period*limit; | ||
89 | 93 | ||
94 | while (prtd->dma_loaded < limit) { | ||
90 | pr_debug("dma_loaded: %d\n", prtd->dma_loaded); | 95 | pr_debug("dma_loaded: %d\n", prtd->dma_loaded); |
91 | 96 | ||
92 | if ((pos + len) > prtd->dma_end) { | 97 | if ((pos + dma_info.period) > prtd->dma_end) { |
93 | len = prtd->dma_end - pos; | 98 | dma_info.period = prtd->dma_end - pos; |
94 | pr_debug("%s: corrected dma len %ld\n", __func__, len); | 99 | pr_debug("%s: corrected dma len %ld\n", |
100 | __func__, dma_info.period); | ||
95 | } | 101 | } |
96 | 102 | ||
97 | ret = s3c2410_dma_enqueue(prtd->params->channel, | 103 | dma_info.buf = pos; |
98 | substream, pos, len); | 104 | prtd->params->ops->prepare(prtd->params->ch, &dma_info); |
99 | 105 | ||
100 | if (ret == 0) { | 106 | prtd->dma_loaded++; |
101 | prtd->dma_loaded++; | 107 | pos += prtd->dma_period; |
102 | pos += prtd->dma_period; | 108 | if (pos >= prtd->dma_end) |
103 | if (pos >= prtd->dma_end) | 109 | pos = prtd->dma_start; |
104 | pos = prtd->dma_start; | ||
105 | } else | ||
106 | break; | ||
107 | } | 110 | } |
108 | 111 | ||
109 | prtd->dma_pos = pos; | 112 | prtd->dma_pos = pos; |
110 | } | 113 | } |
111 | 114 | ||
112 | static void audio_buffdone(struct s3c2410_dma_chan *channel, | 115 | static void audio_buffdone(void *data) |
113 | void *dev_id, int size, | ||
114 | enum s3c2410_dma_buffresult result) | ||
115 | { | 116 | { |
116 | struct snd_pcm_substream *substream = dev_id; | 117 | struct snd_pcm_substream *substream = data; |
117 | struct runtime_data *prtd; | 118 | struct runtime_data *prtd = substream->runtime->private_data; |
118 | 119 | ||
119 | pr_debug("Entered %s\n", __func__); | 120 | pr_debug("Entered %s\n", __func__); |
120 | 121 | ||
121 | if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) | 122 | if (prtd->state & ST_RUNNING) { |
122 | return; | 123 | prtd->dma_pos += prtd->dma_period; |
123 | 124 | if (prtd->dma_pos >= prtd->dma_end) | |
124 | prtd = substream->runtime->private_data; | 125 | prtd->dma_pos = prtd->dma_start; |
125 | 126 | ||
126 | if (substream) | 127 | if (substream) |
127 | snd_pcm_period_elapsed(substream); | 128 | snd_pcm_period_elapsed(substream); |
128 | 129 | ||
129 | spin_lock(&prtd->lock); | 130 | spin_lock(&prtd->lock); |
130 | if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { | 131 | if (!samsung_dma_has_circular()) { |
131 | prtd->dma_loaded--; | 132 | prtd->dma_loaded--; |
132 | dma_enqueue(substream); | 133 | dma_enqueue(substream); |
134 | } | ||
135 | spin_unlock(&prtd->lock); | ||
133 | } | 136 | } |
134 | |||
135 | spin_unlock(&prtd->lock); | ||
136 | } | 137 | } |
137 | 138 | ||
138 | static int dma_hw_params(struct snd_pcm_substream *substream, | 139 | static int dma_hw_params(struct snd_pcm_substream *substream, |
@@ -144,8 +145,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream, | |||
144 | unsigned long totbytes = params_buffer_bytes(params); | 145 | unsigned long totbytes = params_buffer_bytes(params); |
145 | struct s3c_dma_params *dma = | 146 | struct s3c_dma_params *dma = |
146 | snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); | 147 | snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
147 | int ret = 0; | 148 | struct samsung_dma_info dma_info; |
148 | |||
149 | 149 | ||
150 | pr_debug("Entered %s\n", __func__); | 150 | pr_debug("Entered %s\n", __func__); |
151 | 151 | ||
@@ -163,30 +163,26 @@ static int dma_hw_params(struct snd_pcm_substream *substream, | |||
163 | pr_debug("params %p, client %p, channel %d\n", prtd->params, | 163 | pr_debug("params %p, client %p, channel %d\n", prtd->params, |
164 | prtd->params->client, prtd->params->channel); | 164 | prtd->params->client, prtd->params->channel); |
165 | 165 | ||
166 | ret = s3c2410_dma_request(prtd->params->channel, | 166 | prtd->params->ops = samsung_dma_get_ops(); |
167 | prtd->params->client, NULL); | 167 | |
168 | 168 | dma_info.cap = (samsung_dma_has_circular() ? | |
169 | if (ret < 0) { | 169 | DMA_CYCLIC : DMA_SLAVE); |
170 | printk(KERN_ERR "failed to get dma channel\n"); | 170 | dma_info.client = prtd->params->client; |
171 | return ret; | 171 | dma_info.direction = |
172 | } | 172 | (substream->stream == SNDRV_PCM_STREAM_PLAYBACK |
173 | 173 | ? DMA_TO_DEVICE : DMA_FROM_DEVICE); | |
174 | /* use the circular buffering if we have it available. */ | 174 | dma_info.width = prtd->params->dma_size; |
175 | if (s3c_dma_has_circular()) | 175 | dma_info.fifo = prtd->params->dma_addr; |
176 | s3c2410_dma_setflags(prtd->params->channel, | 176 | prtd->params->ch = prtd->params->ops->request( |
177 | S3C2410_DMAF_CIRCULAR); | 177 | prtd->params->channel, &dma_info); |
178 | } | 178 | } |
179 | 179 | ||
180 | s3c2410_dma_set_buffdone_fn(prtd->params->channel, | ||
181 | audio_buffdone); | ||
182 | |||
183 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | 180 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); |
184 | 181 | ||
185 | runtime->dma_bytes = totbytes; | 182 | runtime->dma_bytes = totbytes; |
186 | 183 | ||
187 | spin_lock_irq(&prtd->lock); | 184 | spin_lock_irq(&prtd->lock); |
188 | prtd->dma_loaded = 0; | 185 | prtd->dma_loaded = 0; |
189 | prtd->dma_limit = runtime->hw.periods_min; | ||
190 | prtd->dma_period = params_period_bytes(params); | 186 | prtd->dma_period = params_period_bytes(params); |
191 | prtd->dma_start = runtime->dma_addr; | 187 | prtd->dma_start = runtime->dma_addr; |
192 | prtd->dma_pos = prtd->dma_start; | 188 | prtd->dma_pos = prtd->dma_start; |
@@ -206,7 +202,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream) | |||
206 | snd_pcm_set_runtime_buffer(substream, NULL); | 202 | snd_pcm_set_runtime_buffer(substream, NULL); |
207 | 203 | ||
208 | if (prtd->params) { | 204 | if (prtd->params) { |
209 | s3c2410_dma_free(prtd->params->channel, prtd->params->client); | 205 | prtd->params->ops->release(prtd->params->ch, |
206 | prtd->params->client); | ||
210 | prtd->params = NULL; | 207 | prtd->params = NULL; |
211 | } | 208 | } |
212 | 209 | ||
@@ -225,23 +222,9 @@ static int dma_prepare(struct snd_pcm_substream *substream) | |||
225 | if (!prtd->params) | 222 | if (!prtd->params) |
226 | return 0; | 223 | return 0; |
227 | 224 | ||
228 | /* channel needs configuring for mem=>device, increment memory addr, | ||
229 | * sync to pclk, half-word transfers to the IIS-FIFO. */ | ||
230 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
231 | s3c2410_dma_devconfig(prtd->params->channel, | ||
232 | S3C2410_DMASRC_MEM, | ||
233 | prtd->params->dma_addr); | ||
234 | } else { | ||
235 | s3c2410_dma_devconfig(prtd->params->channel, | ||
236 | S3C2410_DMASRC_HW, | ||
237 | prtd->params->dma_addr); | ||
238 | } | ||
239 | |||
240 | s3c2410_dma_config(prtd->params->channel, | ||
241 | prtd->params->dma_size); | ||
242 | |||
243 | /* flush the DMA channel */ | 225 | /* flush the DMA channel */ |
244 | s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); | 226 | prtd->params->ops->flush(prtd->params->ch); |
227 | |||
245 | prtd->dma_loaded = 0; | 228 | prtd->dma_loaded = 0; |
246 | prtd->dma_pos = prtd->dma_start; | 229 | prtd->dma_pos = prtd->dma_start; |
247 | 230 | ||
@@ -265,14 +248,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd) | |||
265 | case SNDRV_PCM_TRIGGER_RESUME: | 248 | case SNDRV_PCM_TRIGGER_RESUME: |
266 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 249 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
267 | prtd->state |= ST_RUNNING; | 250 | prtd->state |= ST_RUNNING; |
268 | s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); | 251 | prtd->params->ops->trigger(prtd->params->ch); |
269 | break; | 252 | break; |
270 | 253 | ||
271 | case SNDRV_PCM_TRIGGER_STOP: | 254 | case SNDRV_PCM_TRIGGER_STOP: |
272 | case SNDRV_PCM_TRIGGER_SUSPEND: | 255 | case SNDRV_PCM_TRIGGER_SUSPEND: |
273 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 256 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
274 | prtd->state &= ~ST_RUNNING; | 257 | prtd->state &= ~ST_RUNNING; |
275 | s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); | 258 | prtd->params->ops->stop(prtd->params->ch); |
276 | break; | 259 | break; |
277 | 260 | ||
278 | default: | 261 | default: |
@@ -291,21 +274,12 @@ dma_pointer(struct snd_pcm_substream *substream) | |||
291 | struct snd_pcm_runtime *runtime = substream->runtime; | 274 | struct snd_pcm_runtime *runtime = substream->runtime; |
292 | struct runtime_data *prtd = runtime->private_data; | 275 | struct runtime_data *prtd = runtime->private_data; |
293 | unsigned long res; | 276 | unsigned long res; |
294 | dma_addr_t src, dst; | ||
295 | 277 | ||
296 | pr_debug("Entered %s\n", __func__); | 278 | pr_debug("Entered %s\n", __func__); |
297 | 279 | ||
298 | spin_lock(&prtd->lock); | 280 | res = prtd->dma_pos - prtd->dma_start; |
299 | s3c2410_dma_getposition(prtd->params->channel, &src, &dst); | ||
300 | |||
301 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
302 | res = dst - prtd->dma_start; | ||
303 | else | ||
304 | res = src - prtd->dma_start; | ||
305 | |||
306 | spin_unlock(&prtd->lock); | ||
307 | 281 | ||
308 | pr_debug("Pointer %x %x\n", src, dst); | 282 | pr_debug("Pointer offset: %lu\n", res); |
309 | 283 | ||
310 | /* we seem to be getting the odd error from the pcm library due | 284 | /* we seem to be getting the odd error from the pcm library due |
311 | * to out-of-bounds pointers. this is maybe due to the dma engine | 285 | * to out-of-bounds pointers. this is maybe due to the dma engine |
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index c50659269a40..7d1ead77ef21 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h | |||
@@ -6,7 +6,7 @@ | |||
6 | * Free Software Foundation; either version 2 of the License, or (at your | 6 | * Free Software Foundation; either version 2 of the License, or (at your |
7 | * option) any later version. | 7 | * option) any later version. |
8 | * | 8 | * |
9 | * ALSA PCM interface for the Samsung S3C24xx CPU | 9 | * ALSA PCM interface for the Samsung SoC |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #ifndef _S3C_AUDIO_H | 12 | #ifndef _S3C_AUDIO_H |
@@ -17,6 +17,8 @@ struct s3c_dma_params { | |||
17 | int channel; /* Channel ID */ | 17 | int channel; /* Channel ID */ |
18 | dma_addr_t dma_addr; | 18 | dma_addr_t dma_addr; |
19 | int dma_size; /* Size of the DMA transfer */ | 19 | int dma_size; /* Size of the DMA transfer */ |
20 | unsigned ch; | ||
21 | struct samsung_dma_ops *ops; | ||
20 | }; | 22 | }; |
21 | 23 | ||
22 | #endif | 24 | #endif |