pcm-indirect2.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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. /* snd_printk/d() */
  28. #include <sound/core.h>
  29. /* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
  30. * snd_pcm_period_elapsed() */
  31. #include <sound/pcm.h>
  32. #include "pcm-indirect2.h"
  33. #ifdef SND_PCM_INDIRECT2_STAT
  34. /* jiffies */
  35. #include <linux/jiffies.h>
  36. void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
  37. struct snd_pcm_indirect2 *rec)
  38. {
  39. struct snd_pcm_runtime *runtime = substream->runtime;
  40. int i;
  41. int j;
  42. int k;
  43. int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
  44. snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
  45. "irq_occurred: %d\n",
  46. rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
  47. snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
  48. rec->min_multiple);
  49. snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
  50. "firstzerotime: %lu\n",
  51. rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
  52. snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
  53. "length: %d s\n",
  54. rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
  55. snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
  56. "rate: %d Bytes/s = %d Frames/s|Hz\n",
  57. seconds, rec->bytes2hw / seconds,
  58. rec->bytes2hw / 2 / 2 / seconds);
  59. snd_printk(KERN_DEBUG
  60. "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
  61. rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
  62. runtime->rate,
  63. rec->zeros2hw / (rec->hw_buffer_size / 2),
  64. (rec->hw_buffer_size / 2));
  65. snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
  66. rec->pointer_calls, rec->lastdifftime);
  67. snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
  68. rec->sw_data);
  69. snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
  70. k = 0;
  71. for (j = 0; j < 8; j++) {
  72. for (i = j * 8; i < (j + 1) * 8; i++)
  73. if (rec->byte_sizes[i] != 0) {
  74. snd_printk(KERN_DEBUG "%u: %u",
  75. i, rec->byte_sizes[i]);
  76. k++;
  77. }
  78. if (((k % 8) == 0) && (k != 0)) {
  79. snd_printk(KERN_DEBUG "\n");
  80. k = 0;
  81. }
  82. }
  83. snd_printk(KERN_DEBUG "\n");
  84. snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
  85. for (j = 0; j < 8; j++) {
  86. k = 0;
  87. for (i = j * 8; i < (j + 1) * 8; i++)
  88. if (rec->zero_sizes[i] != 0)
  89. snd_printk(KERN_DEBUG "%u: %u",
  90. i, rec->zero_sizes[i]);
  91. else
  92. k++;
  93. if (!k)
  94. snd_printk(KERN_DEBUG "\n");
  95. }
  96. snd_printk(KERN_DEBUG "\n");
  97. snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
  98. for (j = 0; j < 8; j++) {
  99. if (rec->min_adds[j] != 0)
  100. snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
  101. }
  102. snd_printk(KERN_DEBUG "\n");
  103. snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
  104. for (j = 0; j < 8; j++) {
  105. if (rec->mul_adds[j] != 0)
  106. snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
  107. }
  108. snd_printk(KERN_DEBUG "\n");
  109. snd_printk(KERN_DEBUG
  110. "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
  111. rec->zero_times_saved, rec->zero_times_notsaved);
  112. /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
  113. i = 0;
  114. for (j = 0; j < 3750; j++) {
  115. if (rec->zero_times[j] != 0) {
  116. snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
  117. i++;
  118. }
  119. if (((i % 8) == 0) && (i != 0))
  120. snd_printk(KERN_DEBUG "\n");
  121. }
  122. snd_printk(KERN_DEBUG "\n"); */
  123. return;
  124. }
  125. #endif
  126. /*
  127. * _internal_ helper function for playback/capture transfer function
  128. */
  129. static void
  130. snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
  131. struct snd_pcm_indirect2 *rec,
  132. int isplay, int iscopy,
  133. unsigned int bytes)
  134. {
  135. if (rec->min_periods >= 0) {
  136. if (iscopy) {
  137. rec->sw_io += bytes;
  138. if (rec->sw_io >= rec->sw_buffer_size)
  139. rec->sw_io -= rec->sw_buffer_size;
  140. } else if (isplay) {
  141. /* If application does not write data in multiples of
  142. * a period, move sw_data to the next correctly aligned
  143. * position, so that sw_io can converge to it (in the
  144. * next step).
  145. */
  146. if (!rec->check_alignment) {
  147. if (rec->bytes2hw %
  148. snd_pcm_lib_period_bytes(substream)) {
  149. unsigned bytes2hw_aligned =
  150. (1 +
  151. (rec->bytes2hw /
  152. snd_pcm_lib_period_bytes
  153. (substream))) *
  154. snd_pcm_lib_period_bytes
  155. (substream);
  156. rec->sw_data =
  157. bytes2hw_aligned %
  158. rec->sw_buffer_size;
  159. #ifdef SND_PCM_INDIRECT2_STAT
  160. snd_printk(KERN_DEBUG
  161. "STAT: @re-align: aligned "
  162. "bytes2hw to next period "
  163. "size boundary: %d "
  164. "(instead of %d)\n",
  165. bytes2hw_aligned,
  166. rec->bytes2hw);
  167. snd_printk(KERN_DEBUG
  168. "STAT: @re-align: sw_data "
  169. "moves to: %d\n",
  170. rec->sw_data);
  171. #endif
  172. }
  173. rec->check_alignment = 1;
  174. }
  175. /* We are at the end and are copying zeros into the
  176. * fifo.
  177. * Now, we have to make sure that sw_io is increased
  178. * until the position of sw_data: Filling the fifo with
  179. * the first zeros means, the last bytes were played.
  180. */
  181. if (rec->sw_io != rec->sw_data) {
  182. unsigned int diff;
  183. if (rec->sw_data > rec->sw_io)
  184. diff = rec->sw_data - rec->sw_io;
  185. else
  186. diff = (rec->sw_buffer_size -
  187. rec->sw_io) +
  188. rec->sw_data;
  189. if (bytes >= diff)
  190. rec->sw_io = rec->sw_data;
  191. else {
  192. rec->sw_io += bytes;
  193. if (rec->sw_io >= rec->sw_buffer_size)
  194. rec->sw_io -=
  195. rec->sw_buffer_size;
  196. }
  197. }
  198. }
  199. rec->min_period_count += bytes;
  200. if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
  201. rec->min_periods += (rec->min_period_count /
  202. (rec->hw_buffer_size / 2));
  203. #ifdef SND_PCM_INDIRECT2_STAT
  204. if ((rec->min_period_count /
  205. (rec->hw_buffer_size / 2)) > 7)
  206. snd_printk(KERN_DEBUG
  207. "STAT: more than 7 (%d) min_adds "
  208. "at once - too big to save!\n",
  209. (rec->min_period_count /
  210. (rec->hw_buffer_size / 2)));
  211. else
  212. rec->min_adds[(rec->min_period_count /
  213. (rec->hw_buffer_size / 2))]++;
  214. #endif
  215. rec->min_period_count = (rec->min_period_count %
  216. (rec->hw_buffer_size / 2));
  217. }
  218. } else if (isplay && iscopy)
  219. rec->min_periods = 0;
  220. }
  221. /*
  222. * helper function for playback/capture pointer callback
  223. */
  224. snd_pcm_uframes_t
  225. snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
  226. struct snd_pcm_indirect2 *rec)
  227. {
  228. #ifdef SND_PCM_INDIRECT2_STAT
  229. rec->pointer_calls++;
  230. #endif
  231. return bytes_to_frames(substream->runtime, rec->sw_io);
  232. }
  233. /*
  234. * _internal_ helper function for playback interrupt callback
  235. */
  236. static void
  237. snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
  238. struct snd_pcm_indirect2 *rec,
  239. snd_pcm_indirect2_copy_t copy,
  240. snd_pcm_indirect2_zero_t zero)
  241. {
  242. struct snd_pcm_runtime *runtime = substream->runtime;
  243. snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  244. /* runtime->control->appl_ptr: position where ALSA will write next time
  245. * rec->appl_ptr: position where ALSA was last time
  246. * diff: obviously ALSA wrote that much bytes into the intermediate
  247. * buffer since we checked last time
  248. */
  249. snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  250. if (diff) {
  251. #ifdef SND_PCM_INDIRECT2_STAT
  252. rec->lastdifftime = jiffies;
  253. #endif
  254. if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  255. diff += runtime->boundary;
  256. /* number of bytes "added" by ALSA increases the number of
  257. * bytes which are ready to "be transferred to HW"/"played"
  258. * Then, set rec->appl_ptr to not count bytes twice next time.
  259. */
  260. rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  261. rec->appl_ptr = appl_ptr;
  262. }
  263. if (rec->hw_ready && (rec->sw_ready <= 0)) {
  264. unsigned int bytes;
  265. #ifdef SND_PCM_INDIRECT2_STAT
  266. if (rec->firstzerotime == 0) {
  267. rec->firstzerotime = jiffies;
  268. snd_printk(KERN_DEBUG
  269. "STAT: @firstzerotime: mul_elapsed: %d, "
  270. "min_period_count: %d\n",
  271. rec->mul_elapsed, rec->min_period_count);
  272. snd_printk(KERN_DEBUG
  273. "STAT: @firstzerotime: sw_io: %d, "
  274. "sw_data: %d, appl_ptr: %u\n",
  275. rec->sw_io, rec->sw_data,
  276. (unsigned int)appl_ptr);
  277. }
  278. if ((jiffies - rec->firstzerotime) < 3750) {
  279. rec->zero_times[(jiffies - rec->firstzerotime)]++;
  280. rec->zero_times_saved++;
  281. } else
  282. rec->zero_times_notsaved++;
  283. #endif
  284. bytes = zero(substream, rec);
  285. #ifdef SND_PCM_INDIRECT2_STAT
  286. rec->zeros2hw += bytes;
  287. if (bytes < 64)
  288. rec->zero_sizes[bytes]++;
  289. else
  290. snd_printk(KERN_DEBUG
  291. "STAT: %d zero Bytes copied to hardware at "
  292. "once - too big to save!\n",
  293. bytes);
  294. #endif
  295. snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
  296. bytes);
  297. return;
  298. }
  299. while (rec->hw_ready && (rec->sw_ready > 0)) {
  300. /* sw_to_end: max. number of bytes that can be read/take from
  301. * the current position (sw_data) in _one_ step
  302. */
  303. unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  304. /* bytes: number of bytes we have available (for reading) */
  305. unsigned int bytes = rec->sw_ready;
  306. if (sw_to_end < bytes)
  307. bytes = sw_to_end;
  308. if (!bytes)
  309. break;
  310. #ifdef SND_PCM_INDIRECT2_STAT
  311. if (rec->firstbytetime == 0)
  312. rec->firstbytetime = jiffies;
  313. rec->lastbytetime = jiffies;
  314. #endif
  315. /* copy bytes from intermediate buffer position sw_data to the
  316. * HW and return number of bytes actually written
  317. * Furthermore, set hw_ready to 0, if the fifo isn't empty
  318. * now => more could be transferred to fifo
  319. */
  320. bytes = copy(substream, rec, bytes);
  321. rec->bytes2hw += bytes;
  322. #ifdef SND_PCM_INDIRECT2_STAT
  323. if (bytes < 64)
  324. rec->byte_sizes[bytes]++;
  325. else
  326. snd_printk(KERN_DEBUG
  327. "STAT: %d Bytes copied to hardware at once "
  328. "- too big to save!\n",
  329. bytes);
  330. #endif
  331. /* increase sw_data by the number of actually written bytes
  332. * (= number of taken bytes from intermediate buffer)
  333. */
  334. rec->sw_data += bytes;
  335. if (rec->sw_data == rec->sw_buffer_size)
  336. rec->sw_data = 0;
  337. /* now sw_data is the position where ALSA is going to write
  338. * in the intermediate buffer next time = position we are going
  339. * to read from next time
  340. */
  341. snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
  342. bytes);
  343. /* we read bytes from intermediate buffer, so we need to say
  344. * that the number of bytes ready for transfer are decreased
  345. * now
  346. */
  347. rec->sw_ready -= bytes;
  348. }
  349. return;
  350. }
  351. /*
  352. * helper function for playback interrupt routine
  353. */
  354. void
  355. snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
  356. struct snd_pcm_indirect2 *rec,
  357. snd_pcm_indirect2_copy_t copy,
  358. snd_pcm_indirect2_zero_t zero)
  359. {
  360. #ifdef SND_PCM_INDIRECT2_STAT
  361. rec->irq_occured++;
  362. #endif
  363. /* hardware played some bytes, so there is room again (in fifo) */
  364. rec->hw_ready = 1;
  365. /* don't call ack() now, instead call transfer() function directly
  366. * (normally called by ack() )
  367. */
  368. snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
  369. if (rec->min_periods >= rec->min_multiple) {
  370. #ifdef SND_PCM_INDIRECT2_STAT
  371. if ((rec->min_periods / rec->min_multiple) > 7)
  372. snd_printk(KERN_DEBUG
  373. "STAT: more than 7 (%d) mul_adds - too big "
  374. "to save!\n",
  375. (rec->min_periods / rec->min_multiple));
  376. else
  377. rec->mul_adds[(rec->min_periods /
  378. rec->min_multiple)]++;
  379. rec->mul_elapsed_real += (rec->min_periods /
  380. rec->min_multiple);
  381. rec->mul_elapsed++;
  382. #endif
  383. rec->min_periods = (rec->min_periods % rec->min_multiple);
  384. snd_pcm_period_elapsed(substream);
  385. }
  386. }
  387. /*
  388. * _internal_ helper function for capture interrupt callback
  389. */
  390. static void
  391. snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
  392. struct snd_pcm_indirect2 *rec,
  393. snd_pcm_indirect2_copy_t copy,
  394. snd_pcm_indirect2_zero_t null)
  395. {
  396. struct snd_pcm_runtime *runtime = substream->runtime;
  397. snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  398. snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  399. if (diff) {
  400. #ifdef SND_PCM_INDIRECT2_STAT
  401. rec->lastdifftime = jiffies;
  402. #endif
  403. if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  404. diff += runtime->boundary;
  405. rec->sw_ready -= frames_to_bytes(runtime, diff);
  406. rec->appl_ptr = appl_ptr;
  407. }
  408. /* if hardware has something, but the intermediate buffer is full
  409. * => skip contents of buffer
  410. */
  411. if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
  412. unsigned int bytes;
  413. #ifdef SND_PCM_INDIRECT2_STAT
  414. if (rec->firstzerotime == 0) {
  415. rec->firstzerotime = jiffies;
  416. snd_printk(KERN_DEBUG "STAT: (capture) "
  417. "@firstzerotime: mul_elapsed: %d, "
  418. "min_period_count: %d\n",
  419. rec->mul_elapsed, rec->min_period_count);
  420. snd_printk(KERN_DEBUG "STAT: (capture) "
  421. "@firstzerotime: sw_io: %d, sw_data: %d, "
  422. "appl_ptr: %u\n",
  423. rec->sw_io, rec->sw_data,
  424. (unsigned int)appl_ptr);
  425. }
  426. if ((jiffies - rec->firstzerotime) < 3750) {
  427. rec->zero_times[(jiffies - rec->firstzerotime)]++;
  428. rec->zero_times_saved++;
  429. } else
  430. rec->zero_times_notsaved++;
  431. #endif
  432. bytes = null(substream, rec);
  433. #ifdef SND_PCM_INDIRECT2_STAT
  434. rec->zeros2hw += bytes;
  435. if (bytes < 64)
  436. rec->zero_sizes[bytes]++;
  437. else
  438. snd_printk(KERN_DEBUG
  439. "STAT: (capture) %d zero Bytes copied to "
  440. "hardware at once - too big to save!\n",
  441. bytes);
  442. #endif
  443. snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
  444. bytes);
  445. /* report an overrun */
  446. rec->sw_io = SNDRV_PCM_POS_XRUN;
  447. return;
  448. }
  449. while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
  450. /* sw_to_end: max. number of bytes that we can write to the
  451. * intermediate buffer (until it's end)
  452. */
  453. size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
  454. /* bytes: max. number of bytes, which may be copied to the
  455. * intermediate buffer without overflow (in _one_ step)
  456. */
  457. size_t bytes = rec->sw_buffer_size - rec->sw_ready;
  458. /* limit number of bytes (for transfer) by available room in
  459. * the intermediate buffer
  460. */
  461. if (sw_to_end < bytes)
  462. bytes = sw_to_end;
  463. if (!bytes)
  464. break;
  465. #ifdef SND_PCM_INDIRECT2_STAT
  466. if (rec->firstbytetime == 0)
  467. rec->firstbytetime = jiffies;
  468. rec->lastbytetime = jiffies;
  469. #endif
  470. /* copy bytes from the intermediate buffer (position sw_data)
  471. * to the HW at most and return number of bytes actually copied
  472. * from HW
  473. * Furthermore, set hw_ready to 0, if the fifo is empty now.
  474. */
  475. bytes = copy(substream, rec, bytes);
  476. rec->bytes2hw += bytes;
  477. #ifdef SND_PCM_INDIRECT2_STAT
  478. if (bytes < 64)
  479. rec->byte_sizes[bytes]++;
  480. else
  481. snd_printk(KERN_DEBUG
  482. "STAT: (capture) %d Bytes copied to "
  483. "hardware at once - too big to save!\n",
  484. bytes);
  485. #endif
  486. /* increase sw_data by the number of actually copied bytes from
  487. * HW
  488. */
  489. rec->sw_data += bytes;
  490. if (rec->sw_data == rec->sw_buffer_size)
  491. rec->sw_data = 0;
  492. snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
  493. bytes);
  494. /* number of bytes in the intermediate buffer, which haven't
  495. * been fetched by ALSA yet.
  496. */
  497. rec->sw_ready += bytes;
  498. }
  499. return;
  500. }
  501. /*
  502. * helper function for capture interrupt routine
  503. */
  504. void
  505. snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
  506. struct snd_pcm_indirect2 *rec,
  507. snd_pcm_indirect2_copy_t copy,
  508. snd_pcm_indirect2_zero_t null)
  509. {
  510. #ifdef SND_PCM_INDIRECT2_STAT
  511. rec->irq_occured++;
  512. #endif
  513. /* hardware recorded some bytes, so there is something to read from the
  514. * record fifo:
  515. */
  516. rec->hw_ready = 1;
  517. /* don't call ack() now, instead call transfer() function directly
  518. * (normally called by ack() )
  519. */
  520. snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
  521. if (rec->min_periods >= rec->min_multiple) {
  522. #ifdef SND_PCM_INDIRECT2_STAT
  523. if ((rec->min_periods / rec->min_multiple) > 7)
  524. snd_printk(KERN_DEBUG
  525. "STAT: more than 7 (%d) mul_adds - "
  526. "too big to save!\n",
  527. (rec->min_periods / rec->min_multiple));
  528. else
  529. rec->mul_adds[(rec->min_periods /
  530. rec->min_multiple)]++;
  531. rec->mul_elapsed_real += (rec->min_periods /
  532. rec->min_multiple);
  533. rec->mul_elapsed++;
  534. #endif
  535. rec->min_periods = (rec->min_periods % rec->min_multiple);
  536. snd_pcm_period_elapsed(substream);
  537. }
  538. }