passthrough.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. **********************************************************************
  3. * passthrough.c -- Emu10k1 digital passthrough
  4. * Copyright (C) 2001 Juha Yrjölä <jyrjola@cc.hut.fi>
  5. *
  6. **********************************************************************
  7. *
  8. * Date Author Summary of changes
  9. * ---- ------ ------------------
  10. * May 15, 2001 Juha Yrjölä base code release
  11. *
  12. **********************************************************************
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License as
  16. * published by the Free Software Foundation; either version 2 of
  17. * the License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public
  25. * License along with this program; if not, write to the Free
  26. * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
  27. * USA.
  28. *
  29. **********************************************************************
  30. */
  31. #include <linux/module.h>
  32. #include <linux/poll.h>
  33. #include <linux/slab.h>
  34. #include <linux/bitops.h>
  35. #include <asm/io.h>
  36. #include <linux/sched.h>
  37. #include <linux/smp_lock.h>
  38. #include "hwaccess.h"
  39. #include "cardwo.h"
  40. #include "cardwi.h"
  41. #include "recmgr.h"
  42. #include "irqmgr.h"
  43. #include "audio.h"
  44. #include "8010.h"
  45. static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right)
  46. {
  47. unsigned int idx;
  48. ptr[pt->copyptr] = left;
  49. idx = pt->copyptr + PT_SAMPLES/2;
  50. idx %= PT_SAMPLES;
  51. ptr[idx] = right;
  52. }
  53. static inline int pt_can_write(struct pt_data *pt)
  54. {
  55. return pt->blocks_copied < pt->blocks_played + 8;
  56. }
  57. static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock)
  58. {
  59. struct emu10k1_card *card = wavedev->card;
  60. struct pt_data *pt = &card->pt;
  61. if (nonblock && !pt_can_write(pt))
  62. return -EAGAIN;
  63. while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) {
  64. interruptible_sleep_on(&pt->wait);
  65. if (signal_pending(current))
  66. return -ERESTARTSYS;
  67. }
  68. if (pt->state == PT_STATE_INACTIVE)
  69. return -EAGAIN;
  70. return 0;
  71. }
  72. static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock)
  73. {
  74. struct woinst *woinst = wave_dev->woinst;
  75. struct emu10k1_card *card = wave_dev->card;
  76. struct pt_data *pt = &card->pt;
  77. u16 *ptr = (u16 *) card->tankmem.addr;
  78. int i = 0, r;
  79. unsigned long flags;
  80. r = pt_wait_for_write(wave_dev, nonblock);
  81. if (r < 0)
  82. return r;
  83. spin_lock_irqsave(&card->pt.lock, flags);
  84. while (i < PT_BLOCKSAMPLES) {
  85. pt_putsamples(pt, ptr, block[2*i], block[2*i+1]);
  86. if (pt->copyptr == 0)
  87. pt->copyptr = PT_SAMPLES;
  88. pt->copyptr--;
  89. i++;
  90. }
  91. woinst->total_copied += PT_BLOCKSIZE;
  92. pt->blocks_copied++;
  93. if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) {
  94. DPF(2, "activating digital pass-through playback\n");
  95. sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1);
  96. pt->state = PT_STATE_PLAYING;
  97. }
  98. spin_unlock_irqrestore(&card->pt.lock, flags);
  99. return 0;
  100. }
  101. int emu10k1_pt_setup(struct emu10k1_wavedevice *wave_dev)
  102. {
  103. u32 bits;
  104. struct emu10k1_card *card = wave_dev->card;
  105. struct pt_data *pt = &card->pt;
  106. int i;
  107. for (i = 0; i < 3; i++) {
  108. pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0);
  109. if (pt->spcs_to_use & (1 << i)) {
  110. DPD(2, "using S/PDIF port %d\n", i);
  111. bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
  112. SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
  113. 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
  114. if (pt->ac3data)
  115. bits |= SPCS_NOTAUDIODATA;
  116. sblive_writeptr(card, SPCS0 + i, 0, bits);
  117. }
  118. }
  119. return 0;
  120. }
  121. ssize_t emu10k1_pt_write(struct file *file, const char __user *buffer, size_t count)
  122. {
  123. struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
  124. struct emu10k1_card *card = wave_dev->card;
  125. struct pt_data *pt = &card->pt;
  126. int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0;
  127. DPD(3, "emu10k1_pt_write(): %d bytes\n", count);
  128. nonblock = file->f_flags & O_NONBLOCK;
  129. if (card->tankmem.size < PT_SAMPLES*2)
  130. return -EFAULT;
  131. if (pt->state == PT_STATE_INACTIVE) {
  132. DPF(2, "bufptr init\n");
  133. pt->playptr = PT_SAMPLES-1;
  134. pt->copyptr = PT_INITPTR;
  135. pt->blocks_played = pt->blocks_copied = 0;
  136. memset(card->tankmem.addr, 0, card->tankmem.size);
  137. pt->state = PT_STATE_ACTIVATED;
  138. pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL);
  139. pt->prepend_size = 0;
  140. if (pt->buf == NULL)
  141. return -ENOMEM;
  142. emu10k1_pt_setup(wave_dev);
  143. }
  144. if (pt->prepend_size) {
  145. int needed = PT_BLOCKSIZE - pt->prepend_size;
  146. DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed);
  147. if (count < needed) {
  148. copy_from_user(pt->buf + pt->prepend_size, buffer, count);
  149. pt->prepend_size += count;
  150. DPD(3, "prepend size now %d\n", pt->prepend_size);
  151. return count;
  152. }
  153. copy_from_user(pt->buf + pt->prepend_size, buffer, needed);
  154. r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock);
  155. if (r)
  156. return r;
  157. bytes_copied += needed;
  158. pt->prepend_size = 0;
  159. }
  160. blocks = (count-bytes_copied)/PT_BLOCKSIZE;
  161. blocks_copied = 0;
  162. while (blocks > 0) {
  163. u16 __user *bufptr = (u16 __user *) buffer + (bytes_copied/2);
  164. copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE);
  165. r = pt_putblock(wave_dev, (u16 *)pt->buf, nonblock);
  166. if (r) {
  167. if (bytes_copied)
  168. return bytes_copied;
  169. else
  170. return r;
  171. }
  172. bytes_copied += PT_BLOCKSIZE;
  173. blocks--;
  174. blocks_copied++;
  175. }
  176. i = count - bytes_copied;
  177. if (i) {
  178. pt->prepend_size = i;
  179. copy_from_user(pt->buf, buffer + bytes_copied, i);
  180. bytes_copied += i;
  181. DPD(3, "filling prepend buffer with %d bytes", i);
  182. }
  183. return bytes_copied;
  184. }
  185. void emu10k1_pt_stop(struct emu10k1_card *card)
  186. {
  187. struct pt_data *pt = &card->pt;
  188. int i;
  189. if (pt->state != PT_STATE_INACTIVE) {
  190. DPF(2, "digital pass-through stopped\n");
  191. sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + pt->enable_gpr, 0, 0);
  192. for (i = 0; i < 3; i++) {
  193. if (pt->spcs_to_use & (1 << i))
  194. sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]);
  195. }
  196. pt->state = PT_STATE_INACTIVE;
  197. kfree(pt->buf);
  198. }
  199. }
  200. void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev)
  201. {
  202. struct woinst *woinst = wave_dev->woinst;
  203. struct pt_data *pt = &wave_dev->card->pt;
  204. u32 pos;
  205. if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) {
  206. pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0);
  207. if (pos > PT_BLOCKSAMPLES)
  208. pos = PT_BLOCKSAMPLES;
  209. pos = 4 * (PT_BLOCKSAMPLES - pos);
  210. } else
  211. pos = 0;
  212. woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos;
  213. woinst->buffer.hw_pos = pos;
  214. }