diff options
author | Joachim Foerster <JOFT@gmx.de> | 2007-11-05 10:06:01 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-01-31 11:29:15 -0500 |
commit | a9f00d8df2115b396f13ea74b835f18215a871cc (patch) | |
tree | 2af215341e8ff1ca72de73cabda2e31d122db025 /sound/drivers/pcm-indirect2.c | |
parent | f1f208d0b4fb79f99d2ca5031c61ff5b52e42e75 (diff) |
[ALSA] Xilinx ML403 AC97 Controller Reference device driver
Add ALSA support for the opb_ac97_controller_ref_v1_00_a ip core found
in Xilinx' ML403 reference design.
Known issue: Currently this driver hits a WARN_ON_ONCE(1) statement in
kernel/irq/resend.c (line 70). According to Linus
(http://lkml.org/lkml/2007/8/5/5) this may be ignored, right? I haven't
had a look into this 'problem' yet.
Signed-off-by: Joachim Foerster <JOFT@gmx.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/drivers/pcm-indirect2.c')
-rw-r--r-- | sound/drivers/pcm-indirect2.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c new file mode 100644 index 000000000000..6a829cd03dde --- /dev/null +++ b/sound/drivers/pcm-indirect2.c | |||
@@ -0,0 +1,591 @@ | |||
1 | /* | ||
2 | * Helper functions for indirect PCM data transfer to a simple FIFO in | ||
3 | * hardware (small, no possibility to read "hardware io position", | ||
4 | * updating position done by interrupt, ...) | ||
5 | * | ||
6 | * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> | ||
7 | * | ||
8 | * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by | ||
9 | * | ||
10 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
11 | * Jaroslav Kysela <perex@suse.cz> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
26 | */ | ||
27 | |||
28 | /* #dependency of sound/core.h# */ | ||
29 | #include <sound/driver.h> | ||
30 | /* snd_printk/d() */ | ||
31 | #include <sound/core.h> | ||
32 | /* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t | ||
33 | * snd_pcm_period_elapsed() */ | ||
34 | #include <sound/pcm.h> | ||
35 | |||
36 | #include "pcm-indirect2.h" | ||
37 | |||
38 | #ifdef SND_PCM_INDIRECT2_STAT | ||
39 | /* jiffies */ | ||
40 | #include <linux/jiffies.h> | ||
41 | |||
42 | void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, | ||
43 | struct snd_pcm_indirect2 *rec) | ||
44 | { | ||
45 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
46 | int i; | ||
47 | int j; | ||
48 | int k; | ||
49 | int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ; | ||
50 | |||
51 | snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, " | ||
52 | "irq_occured: %d\n", | ||
53 | rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured); | ||
54 | snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n", | ||
55 | rec->min_multiple); | ||
56 | snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, " | ||
57 | "firstzerotime: %lu\n", | ||
58 | rec->firstbytetime, rec->lastbytetime, rec->firstzerotime); | ||
59 | snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) " | ||
60 | "length: %d s\n", | ||
61 | rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate); | ||
62 | snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => " | ||
63 | "rate: %d Bytes/s = %d Frames/s|Hz\n", | ||
64 | seconds, rec->bytes2hw / seconds, | ||
65 | rec->bytes2hw / 2 / 2 / seconds); | ||
66 | snd_printk(KERN_DEBUG | ||
67 | "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n", | ||
68 | rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) / | ||
69 | runtime->rate, | ||
70 | rec->zeros2hw / (rec->hw_buffer_size / 2), | ||
71 | (rec->hw_buffer_size / 2)); | ||
72 | snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n", | ||
73 | rec->pointer_calls, rec->lastdifftime); | ||
74 | snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io, | ||
75 | rec->sw_data); | ||
76 | snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n"); | ||
77 | k = 0; | ||
78 | for (j = 0; j < 8; j++) { | ||
79 | for (i = j * 8; i < (j + 1) * 8; i++) | ||
80 | if (rec->byte_sizes[i] != 0) { | ||
81 | snd_printk(KERN_DEBUG "%u: %u", | ||
82 | i, rec->byte_sizes[i]); | ||
83 | k++; | ||
84 | } | ||
85 | if (((k % 8) == 0) && (k != 0)) { | ||
86 | snd_printk(KERN_DEBUG "\n"); | ||
87 | k = 0; | ||
88 | } | ||
89 | } | ||
90 | snd_printk(KERN_DEBUG "\n"); | ||
91 | snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n"); | ||
92 | for (j = 0; j < 8; j++) { | ||
93 | k = 0; | ||
94 | for (i = j * 8; i < (j + 1) * 8; i++) | ||
95 | if (rec->zero_sizes[i] != 0) | ||
96 | snd_printk(KERN_DEBUG "%u: %u", | ||
97 | i, rec->zero_sizes[i]); | ||
98 | else | ||
99 | k++; | ||
100 | if (!k) | ||
101 | snd_printk(KERN_DEBUG "\n"); | ||
102 | } | ||
103 | snd_printk(KERN_DEBUG "\n"); | ||
104 | snd_printk(KERN_DEBUG "STAT: min_adds[]:\n"); | ||
105 | for (j = 0; j < 8; j++) { | ||
106 | if (rec->min_adds[j] != 0) | ||
107 | snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]); | ||
108 | } | ||
109 | snd_printk(KERN_DEBUG "\n"); | ||
110 | snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n"); | ||
111 | for (j = 0; j < 8; j++) { | ||
112 | if (rec->mul_adds[j] != 0) | ||
113 | snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]); | ||
114 | } | ||
115 | snd_printk(KERN_DEBUG "\n"); | ||
116 | snd_printk(KERN_DEBUG | ||
117 | "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n", | ||
118 | rec->zero_times_saved, rec->zero_times_notsaved); | ||
119 | /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n"); | ||
120 | i = 0; | ||
121 | for (j = 0; j < 3750; j++) { | ||
122 | if (rec->zero_times[j] != 0) { | ||
123 | snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]); | ||
124 | i++; | ||
125 | } | ||
126 | if (((i % 8) == 0) && (i != 0)) | ||
127 | snd_printk(KERN_DEBUG "\n"); | ||
128 | } | ||
129 | snd_printk(KERN_DEBUG "\n"); */ | ||
130 | return; | ||
131 | } | ||
132 | #endif | ||
133 | |||
134 | /* | ||
135 | * _internal_ helper function for playback/capture transfer function | ||
136 | */ | ||
137 | static void | ||
138 | snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream, | ||
139 | struct snd_pcm_indirect2 *rec, | ||
140 | int isplay, int iscopy, | ||
141 | unsigned int bytes) | ||
142 | { | ||
143 | if (rec->min_periods >= 0) { | ||
144 | if (iscopy) { | ||
145 | rec->sw_io += bytes; | ||
146 | if (rec->sw_io >= rec->sw_buffer_size) | ||
147 | rec->sw_io -= rec->sw_buffer_size; | ||
148 | } else if (isplay) { | ||
149 | /* If application does not write data in multiples of | ||
150 | * a period, move sw_data to the next correctly aligned | ||
151 | * position, so that sw_io can converge to it (in the | ||
152 | * next step). | ||
153 | */ | ||
154 | if (!rec->check_alignment) { | ||
155 | if (rec->bytes2hw % | ||
156 | snd_pcm_lib_period_bytes(substream)) { | ||
157 | unsigned bytes2hw_aligned = | ||
158 | (1 + | ||
159 | (rec->bytes2hw / | ||
160 | snd_pcm_lib_period_bytes | ||
161 | (substream))) * | ||
162 | snd_pcm_lib_period_bytes | ||
163 | (substream); | ||
164 | rec->sw_data = | ||
165 | bytes2hw_aligned % | ||
166 | rec->sw_buffer_size; | ||
167 | #ifdef SND_PCM_INDIRECT2_STAT | ||
168 | snd_printk(KERN_DEBUG | ||
169 | "STAT: @re-align: aligned " | ||
170 | "bytes2hw to next period " | ||
171 | "size boundary: %d " | ||
172 | "(instead of %d)\n", | ||
173 | bytes2hw_aligned, | ||
174 | rec->bytes2hw); | ||
175 | snd_printk(KERN_DEBUG | ||
176 | "STAT: @re-align: sw_data " | ||
177 | "moves to: %d\n", | ||
178 | rec->sw_data); | ||
179 | #endif | ||
180 | } | ||
181 | rec->check_alignment = 1; | ||
182 | } | ||
183 | /* We are at the end and are copying zeros into the | ||
184 | * fifo. | ||
185 | * Now, we have to make sure that sw_io is increased | ||
186 | * until the position of sw_data: Filling the fifo with | ||
187 | * the first zeros means, the last bytes were played. | ||
188 | */ | ||
189 | if (rec->sw_io != rec->sw_data) { | ||
190 | unsigned int diff; | ||
191 | if (rec->sw_data > rec->sw_io) | ||
192 | diff = rec->sw_data - rec->sw_io; | ||
193 | else | ||
194 | diff = (rec->sw_buffer_size - | ||
195 | rec->sw_io) + | ||
196 | rec->sw_data; | ||
197 | if (bytes >= diff) | ||
198 | rec->sw_io = rec->sw_data; | ||
199 | else { | ||
200 | rec->sw_io += bytes; | ||
201 | if (rec->sw_io >= rec->sw_buffer_size) | ||
202 | rec->sw_io -= | ||
203 | rec->sw_buffer_size; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | rec->min_period_count += bytes; | ||
208 | if (rec->min_period_count >= (rec->hw_buffer_size / 2)) { | ||
209 | rec->min_periods += (rec->min_period_count / | ||
210 | (rec->hw_buffer_size / 2)); | ||
211 | #ifdef SND_PCM_INDIRECT2_STAT | ||
212 | if ((rec->min_period_count / | ||
213 | (rec->hw_buffer_size / 2)) > 7) | ||
214 | snd_printk(KERN_DEBUG | ||
215 | "STAT: more than 7 (%d) min_adds " | ||
216 | "at once - too big to save!\n", | ||
217 | (rec->min_period_count / | ||
218 | (rec->hw_buffer_size / 2))); | ||
219 | else | ||
220 | rec->min_adds[(rec->min_period_count / | ||
221 | (rec->hw_buffer_size / 2))]++; | ||
222 | #endif | ||
223 | rec->min_period_count = (rec->min_period_count % | ||
224 | (rec->hw_buffer_size / 2)); | ||
225 | } | ||
226 | } else if (isplay && iscopy) | ||
227 | rec->min_periods = 0; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * helper function for playback/capture pointer callback | ||
232 | */ | ||
233 | snd_pcm_uframes_t | ||
234 | snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, | ||
235 | struct snd_pcm_indirect2 *rec) | ||
236 | { | ||
237 | #ifdef SND_PCM_INDIRECT2_STAT | ||
238 | rec->pointer_calls++; | ||
239 | #endif | ||
240 | return bytes_to_frames(substream->runtime, rec->sw_io); | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * _internal_ helper function for playback interrupt callback | ||
245 | */ | ||
246 | static void | ||
247 | snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream, | ||
248 | struct snd_pcm_indirect2 *rec, | ||
249 | snd_pcm_indirect2_copy_t copy, | ||
250 | snd_pcm_indirect2_zero_t zero) | ||
251 | { | ||
252 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
253 | snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; | ||
254 | |||
255 | /* runtime->control->appl_ptr: position where ALSA will write next time | ||
256 | * rec->appl_ptr: position where ALSA was last time | ||
257 | * diff: obviously ALSA wrote that much bytes into the intermediate | ||
258 | * buffer since we checked last time | ||
259 | */ | ||
260 | snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; | ||
261 | |||
262 | if (diff) { | ||
263 | #ifdef SND_PCM_INDIRECT2_STAT | ||
264 | rec->lastdifftime = jiffies; | ||
265 | #endif | ||
266 | if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) | ||
267 | diff += runtime->boundary; | ||
268 | /* number of bytes "added" by ALSA increases the number of | ||
269 | * bytes which are ready to "be transfered to HW"/"played" | ||
270 | * Then, set rec->appl_ptr to not count bytes twice next time. | ||
271 | */ | ||
272 | rec->sw_ready += (int)frames_to_bytes(runtime, diff); | ||
273 | rec->appl_ptr = appl_ptr; | ||
274 | } | ||
275 | if (rec->hw_ready && (rec->sw_ready <= 0)) { | ||
276 | unsigned int bytes; | ||
277 | |||
278 | #ifdef SND_PCM_INDIRECT2_STAT | ||
279 | if (rec->firstzerotime == 0) { | ||
280 | rec->firstzerotime = jiffies; | ||
281 | snd_printk(KERN_DEBUG | ||
282 | "STAT: @firstzerotime: mul_elapsed: %d, " | ||
283 | "min_period_count: %d\n", | ||
284 | rec->mul_elapsed, rec->min_period_count); | ||
285 | snd_printk(KERN_DEBUG | ||
286 | "STAT: @firstzerotime: sw_io: %d, " | ||
287 | "sw_data: %d, appl_ptr: %u\n", | ||
288 | rec->sw_io, rec->sw_data, | ||
289 | (unsigned int)appl_ptr); | ||
290 | } | ||
291 | if ((jiffies - rec->firstzerotime) < 3750) { | ||
292 | rec->zero_times[(jiffies - rec->firstzerotime)]++; | ||
293 | rec->zero_times_saved++; | ||
294 | } else | ||
295 | rec->zero_times_notsaved++; | ||
296 | #endif | ||
297 | bytes = zero(substream, rec); | ||
298 | |||
299 | #ifdef SND_PCM_INDIRECT2_STAT | ||
300 | rec->zeros2hw += bytes; | ||
301 | if (bytes < 64) | ||
302 | rec->zero_sizes[bytes]++; | ||
303 | else | ||
304 | snd_printk(KERN_DEBUG | ||
305 | "STAT: %d zero Bytes copied to hardware at " | ||
306 | "once - too big to save!\n", | ||
307 | bytes); | ||
308 | #endif | ||
309 | snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0, | ||
310 | bytes); | ||
311 | return; | ||
312 | } | ||
313 | while (rec->hw_ready && (rec->sw_ready > 0)) { | ||
314 | /* sw_to_end: max. number of bytes that can be read/take from | ||
315 | * the current position (sw_data) in _one_ step | ||
316 | */ | ||
317 | unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; | ||
318 | |||
319 | /* bytes: number of bytes we have available (for reading) */ | ||
320 | unsigned int bytes = rec->sw_ready; | ||
321 | |||
322 | if (sw_to_end < bytes) | ||
323 | bytes = sw_to_end; | ||
324 | if (!bytes) | ||
325 | break; | ||
326 | |||
327 | #ifdef SND_PCM_INDIRECT2_STAT | ||
328 | if (rec->firstbytetime == 0) | ||
329 | rec->firstbytetime = jiffies; | ||
330 | rec->lastbytetime = jiffies; | ||
331 | #endif | ||
332 | /* copy bytes from intermediate buffer position sw_data to the | ||
333 | * HW and return number of bytes actually written | ||
334 | * Furthermore, set hw_ready to 0, if the fifo isn't empty | ||
335 | * now => more could be transfered to fifo | ||
336 | */ | ||
337 | bytes = copy(substream, rec, bytes); | ||
338 | rec->bytes2hw += bytes; | ||
339 | |||
340 | #ifdef SND_PCM_INDIRECT2_STAT | ||
341 | if (bytes < 64) | ||
342 | rec->byte_sizes[bytes]++; | ||
343 | else | ||
344 | snd_printk(KERN_DEBUG | ||
345 | "STAT: %d Bytes copied to hardware at once " | ||
346 | "- too big to save!\n", | ||
347 | bytes); | ||
348 | #endif | ||
349 | /* increase sw_data by the number of actually written bytes | ||
350 | * (= number of taken bytes from intermediate buffer) | ||
351 | */ | ||
352 | rec->sw_data += bytes; | ||
353 | if (rec->sw_data == rec->sw_buffer_size) | ||
354 | rec->sw_data = 0; | ||
355 | /* now sw_data is the position where ALSA is going to write | ||
356 | * in the intermediate buffer next time = position we are going | ||
357 | * to read from next time | ||
358 | */ | ||
359 | |||
360 | snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1, | ||
361 | bytes); | ||
362 | |||
363 | /* we read bytes from intermediate buffer, so we need to say | ||
364 | * that the number of bytes ready for transfer are decreased | ||
365 | * now | ||
366 | */ | ||
367 | rec->sw_ready -= bytes; | ||
368 | } | ||
369 | return; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * helper function for playback interrupt routine | ||
374 | */ | ||
375 | void | ||
376 | snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, | ||
377 | struct snd_pcm_indirect2 *rec, | ||
378 | snd_pcm_indirect2_copy_t copy, | ||
379 | snd_pcm_indirect2_zero_t zero) | ||
380 | { | ||
381 | #ifdef SND_PCM_INDIRECT2_STAT | ||
382 | rec->irq_occured++; | ||
383 | #endif | ||
384 | /* hardware played some bytes, so there is room again (in fifo) */ | ||
385 | rec->hw_ready = 1; | ||
386 | |||
387 | /* don't call ack() now, instead call transfer() function directly | ||
388 | * (normally called by ack() ) | ||
389 | */ | ||
390 | snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero); | ||
391 | |||
392 | if (rec->min_periods >= rec->min_multiple) { | ||
393 | #ifdef SND_PCM_INDIRECT2_STAT | ||
394 | if ((rec->min_periods / rec->min_multiple) > 7) | ||
395 | snd_printk(KERN_DEBUG | ||
396 | "STAT: more than 7 (%d) mul_adds - too big " | ||
397 | "to save!\n", | ||
398 | (rec->min_periods / rec->min_multiple)); | ||
399 | else | ||
400 | rec->mul_adds[(rec->min_periods / | ||
401 | rec->min_multiple)]++; | ||
402 | rec->mul_elapsed_real += (rec->min_periods / | ||
403 | rec->min_multiple); | ||
404 | rec->mul_elapsed++; | ||
405 | #endif | ||
406 | rec->min_periods = 0; | ||
407 | snd_pcm_period_elapsed(substream); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * _internal_ helper function for capture interrupt callback | ||
413 | */ | ||
414 | static void | ||
415 | snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream, | ||
416 | struct snd_pcm_indirect2 *rec, | ||
417 | snd_pcm_indirect2_copy_t copy, | ||
418 | snd_pcm_indirect2_zero_t null) | ||
419 | { | ||
420 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
421 | snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; | ||
422 | snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; | ||
423 | |||
424 | if (diff) { | ||
425 | #ifdef SND_PCM_INDIRECT2_STAT | ||
426 | rec->lastdifftime = jiffies; | ||
427 | #endif | ||
428 | if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) | ||
429 | diff += runtime->boundary; | ||
430 | rec->sw_ready -= frames_to_bytes(runtime, diff); | ||
431 | rec->appl_ptr = appl_ptr; | ||
432 | } | ||
433 | /* if hardware has something, but the intermediate buffer is full | ||
434 | * => skip contents of buffer | ||
435 | */ | ||
436 | if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) { | ||
437 | unsigned int bytes; | ||
438 | |||
439 | #ifdef SND_PCM_INDIRECT2_STAT | ||
440 | if (rec->firstzerotime == 0) { | ||
441 | rec->firstzerotime = jiffies; | ||
442 | snd_printk(KERN_DEBUG "STAT: (capture) " | ||
443 | "@firstzerotime: mul_elapsed: %d, " | ||
444 | "min_period_count: %d\n", | ||
445 | rec->mul_elapsed, rec->min_period_count); | ||
446 | snd_printk(KERN_DEBUG "STAT: (capture) " | ||
447 | "@firstzerotime: sw_io: %d, sw_data: %d, " | ||
448 | "appl_ptr: %u\n", | ||
449 | rec->sw_io, rec->sw_data, | ||
450 | (unsigned int)appl_ptr); | ||
451 | } | ||
452 | if ((jiffies - rec->firstzerotime) < 3750) { | ||
453 | rec->zero_times[(jiffies - rec->firstzerotime)]++; | ||
454 | rec->zero_times_saved++; | ||
455 | } else | ||
456 | rec->zero_times_notsaved++; | ||
457 | #endif | ||
458 | bytes = null(substream, rec); | ||
459 | |||
460 | #ifdef SND_PCM_INDIRECT2_STAT | ||
461 | rec->zeros2hw += bytes; | ||
462 | if (bytes < 64) | ||
463 | rec->zero_sizes[bytes]++; | ||
464 | else | ||
465 | snd_printk(KERN_DEBUG | ||
466 | "STAT: (capture) %d zero Bytes copied to " | ||
467 | "hardware at once - too big to save!\n", | ||
468 | bytes); | ||
469 | #endif | ||
470 | snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0, | ||
471 | bytes); | ||
472 | /* report an overrun */ | ||
473 | rec->sw_io = SNDRV_PCM_POS_XRUN; | ||
474 | return; | ||
475 | } | ||
476 | while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) { | ||
477 | /* sw_to_end: max. number of bytes that we can write to the | ||
478 | * intermediate buffer (until it's end) | ||
479 | */ | ||
480 | size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; | ||
481 | |||
482 | /* bytes: max. number of bytes, which may be copied to the | ||
483 | * intermediate buffer without overflow (in _one_ step) | ||
484 | */ | ||
485 | size_t bytes = rec->sw_buffer_size - rec->sw_ready; | ||
486 | |||
487 | /* limit number of bytes (for transfer) by available room in | ||
488 | * the intermediate buffer | ||
489 | */ | ||
490 | if (sw_to_end < bytes) | ||
491 | bytes = sw_to_end; | ||
492 | if (!bytes) | ||
493 | break; | ||
494 | |||
495 | #ifdef SND_PCM_INDIRECT2_STAT | ||
496 | if (rec->firstbytetime == 0) | ||
497 | rec->firstbytetime = jiffies; | ||
498 | rec->lastbytetime = jiffies; | ||
499 | #endif | ||
500 | /* copy bytes from the intermediate buffer (position sw_data) | ||
501 | * to the HW at most and return number of bytes actually copied | ||
502 | * from HW | ||
503 | * Furthermore, set hw_ready to 0, if the fifo is empty now. | ||
504 | */ | ||
505 | bytes = copy(substream, rec, bytes); | ||
506 | rec->bytes2hw += bytes; | ||
507 | |||
508 | #ifdef SND_PCM_INDIRECT2_STAT | ||
509 | if (bytes < 64) | ||
510 | rec->byte_sizes[bytes]++; | ||
511 | else | ||
512 | snd_printk(KERN_DEBUG | ||
513 | "STAT: (capture) %d Bytes copied to " | ||
514 | "hardware at once - too big to save!\n", | ||
515 | bytes); | ||
516 | #endif | ||
517 | /* increase sw_data by the number of actually copied bytes from | ||
518 | * HW | ||
519 | */ | ||
520 | rec->sw_data += bytes; | ||
521 | if (rec->sw_data == rec->sw_buffer_size) | ||
522 | rec->sw_data = 0; | ||
523 | |||
524 | snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1, | ||
525 | bytes); | ||
526 | |||
527 | /* number of bytes in the intermediate buffer, which haven't | ||
528 | * been fetched by ALSA yet. | ||
529 | */ | ||
530 | rec->sw_ready += bytes; | ||
531 | } | ||
532 | return; | ||
533 | } | ||
534 | |||
535 | /* | ||
536 | * helper function for capture interrupt routine | ||
537 | */ | ||
538 | void | ||
539 | snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, | ||
540 | struct snd_pcm_indirect2 *rec, | ||
541 | snd_pcm_indirect2_copy_t copy, | ||
542 | snd_pcm_indirect2_zero_t null) | ||
543 | { | ||
544 | #ifdef SND_PCM_INDIRECT2_STAT | ||
545 | rec->irq_occured++; | ||
546 | #endif | ||
547 | /* hardware recorded some bytes, so there is something to read from the | ||
548 | * record fifo: | ||
549 | */ | ||
550 | rec->hw_ready = 1; | ||
551 | |||
552 | /* don't call ack() now, instead call transfer() function directly | ||
553 | * (normally called by ack() ) | ||
554 | */ | ||
555 | snd_pcm_indirect2_capture_transfer(substream, rec, copy, null); | ||
556 | |||
557 | if (rec->min_periods >= rec->min_multiple) { | ||
558 | |||
559 | #ifdef SND_PCM_INDIRECT2_STAT | ||
560 | if ((rec->min_periods / rec->min_multiple) > 7) | ||
561 | snd_printk(KERN_DEBUG | ||
562 | "STAT: more than 7 (%d) mul_adds - " | ||
563 | "too big to save!\n", | ||
564 | (rec->min_periods / rec->min_multiple)); | ||
565 | else | ||
566 | rec->mul_adds[(rec->min_periods / | ||
567 | rec->min_multiple)]++; | ||
568 | rec->mul_elapsed_real += (rec->min_periods / | ||
569 | rec->min_multiple); | ||
570 | rec->mul_elapsed++; | ||
571 | |||
572 | if (!(rec->mul_elapsed % 4)) { | ||
573 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
574 | unsigned int appl_ptr = | ||
575 | frames_to_bytes(runtime, | ||
576 | (unsigned int)runtime->control-> | ||
577 | appl_ptr) % rec->sw_buffer_size; | ||
578 | int diff = rec->sw_data - appl_ptr; | ||
579 | if (diff < 0) | ||
580 | diff += rec->sw_buffer_size; | ||
581 | snd_printk(KERN_DEBUG | ||
582 | "STAT: mul_elapsed: %d, sw_data: %u, " | ||
583 | "appl_ptr (bytes): %u, diff: %d\n", | ||
584 | rec->mul_elapsed, rec->sw_data, appl_ptr, | ||
585 | diff); | ||
586 | } | ||
587 | #endif | ||
588 | rec->min_periods = 0; | ||
589 | snd_pcm_period_elapsed(substream); | ||
590 | } | ||
591 | } | ||