2 **********************************************************************
3 * passthrough.c -- Emu10k1 digital passthrough
4 * Copyright (C) 2001 Juha Yrjölä <jyrjola@cc.hut.fi>
6 **********************************************************************
8 * Date Author Summary of changes
9 * ---- ------ ------------------
10 * May 15, 2001 Juha Yrjölä base code release
12 **********************************************************************
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.
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.
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,
29 **********************************************************************
32 #include <linux/module.h>
33 #include <linux/poll.h>
34 #include <linux/slab.h>
35 #include <linux/bitops.h>
37 #include <linux/sched.h>
38 #include <linux/smp_lock.h>
48 static void pt_putsamples(struct pt_data
*pt
, u16
*ptr
, u16 left
, u16 right
)
52 ptr
[pt
->copyptr
] = left
;
53 idx
= pt
->copyptr
+ PT_SAMPLES
/2;
58 static inline int pt_can_write(struct pt_data
*pt
)
60 return pt
->blocks_copied
< pt
->blocks_played
+ 8;
63 static int pt_wait_for_write(struct emu10k1_wavedevice
*wavedev
, int nonblock
)
65 struct emu10k1_card
*card
= wavedev
->card
;
66 struct pt_data
*pt
= &card
->pt
;
68 if (nonblock
&& !pt_can_write(pt
))
70 while (!pt_can_write(pt
) && pt
->state
!= PT_STATE_INACTIVE
) {
71 interruptible_sleep_on(&pt
->wait
);
72 if (signal_pending(current
))
75 if (pt
->state
== PT_STATE_INACTIVE
)
81 static int pt_putblock(struct emu10k1_wavedevice
*wave_dev
, u16
*block
, int nonblock
)
83 struct woinst
*woinst
= wave_dev
->woinst
;
84 struct emu10k1_card
*card
= wave_dev
->card
;
85 struct pt_data
*pt
= &card
->pt
;
86 u16
*ptr
= (u16
*) card
->tankmem
.addr
;
90 r
= pt_wait_for_write(wave_dev
, nonblock
);
93 spin_lock_irqsave(&card
->pt
.lock
, flags
);
94 while (i
< PT_BLOCKSAMPLES
) {
95 pt_putsamples(pt
, ptr
, block
[2*i
], block
[2*i
+1]);
97 pt
->copyptr
= PT_SAMPLES
;
101 woinst
->total_copied
+= PT_BLOCKSIZE
;
103 if (pt
->blocks_copied
>= 4 && pt
->state
!= PT_STATE_PLAYING
) {
104 DPF(2, "activating digital pass-through playback\n");
105 sblive_writeptr(card
, GPR_BASE
+ pt
->enable_gpr
, 0, 1);
106 pt
->state
= PT_STATE_PLAYING
;
108 spin_unlock_irqrestore(&card
->pt
.lock
, flags
);
112 int emu10k1_pt_setup(struct emu10k1_wavedevice
*wave_dev
)
115 struct emu10k1_card
*card
= wave_dev
->card
;
116 struct pt_data
*pt
= &card
->pt
;
119 for (i
= 0; i
< 3; i
++) {
120 pt
->old_spcs
[i
] = sblive_readptr(card
, SPCS0
+ i
, 0);
121 if (pt
->spcs_to_use
& (1 << i
)) {
122 DPD(2, "using S/PDIF port %d\n", i
);
123 bits
= SPCS_CLKACCY_1000PPM
| SPCS_SAMPLERATE_48
|
124 SPCS_CHANNELNUM_LEFT
| SPCS_SOURCENUM_UNSPEC
| SPCS_GENERATIONSTATUS
|
125 0x00001200 | SPCS_EMPHASIS_NONE
| SPCS_COPYRIGHT
;
127 bits
|= SPCS_NOTAUDIODATA
;
128 sblive_writeptr(card
, SPCS0
+ i
, 0, bits
);
134 ssize_t
emu10k1_pt_write(struct file
*file
, const char __user
*buffer
, size_t count
)
136 struct emu10k1_wavedevice
*wave_dev
= (struct emu10k1_wavedevice
*) file
->private_data
;
137 struct emu10k1_card
*card
= wave_dev
->card
;
138 struct pt_data
*pt
= &card
->pt
;
139 int nonblock
, i
, r
, blocks
, blocks_copied
, bytes_copied
= 0;
141 DPD(3, "emu10k1_pt_write(): %d bytes\n", count
);
143 nonblock
= file
->f_flags
& O_NONBLOCK
;
145 if (card
->tankmem
.size
< PT_SAMPLES
*2)
147 if (pt
->state
== PT_STATE_INACTIVE
) {
148 DPF(2, "bufptr init\n");
149 pt
->playptr
= PT_SAMPLES
-1;
150 pt
->copyptr
= PT_INITPTR
;
151 pt
->blocks_played
= pt
->blocks_copied
= 0;
152 memset(card
->tankmem
.addr
, 0, card
->tankmem
.size
);
153 pt
->state
= PT_STATE_ACTIVATED
;
154 pt
->buf
= kmalloc(PT_BLOCKSIZE
, GFP_KERNEL
);
155 pt
->prepend_size
= 0;
158 emu10k1_pt_setup(wave_dev
);
160 if (pt
->prepend_size
) {
161 int needed
= PT_BLOCKSIZE
- pt
->prepend_size
;
163 DPD(3, "prepend size %d, prepending %d bytes\n", pt
->prepend_size
, needed
);
164 if (count
< needed
) {
165 if (copy_from_user(pt
->buf
+ pt
->prepend_size
,
168 pt
->prepend_size
+= count
;
169 DPD(3, "prepend size now %d\n", pt
->prepend_size
);
172 if (copy_from_user(pt
->buf
+ pt
->prepend_size
, buffer
, needed
))
174 r
= pt_putblock(wave_dev
, (u16
*) pt
->buf
, nonblock
);
177 bytes_copied
+= needed
;
178 pt
->prepend_size
= 0;
180 blocks
= (count
-bytes_copied
)/PT_BLOCKSIZE
;
183 u16 __user
*bufptr
= (u16 __user
*) buffer
+ (bytes_copied
/2);
184 if (copy_from_user(pt
->buf
, bufptr
, PT_BLOCKSIZE
))
186 r
= pt_putblock(wave_dev
, (u16
*)pt
->buf
, nonblock
);
193 bytes_copied
+= PT_BLOCKSIZE
;
197 i
= count
- bytes_copied
;
199 pt
->prepend_size
= i
;
200 if (copy_from_user(pt
->buf
, buffer
+ bytes_copied
, i
))
203 DPD(3, "filling prepend buffer with %d bytes", i
);
208 void emu10k1_pt_stop(struct emu10k1_card
*card
)
210 struct pt_data
*pt
= &card
->pt
;
213 if (pt
->state
!= PT_STATE_INACTIVE
) {
214 DPF(2, "digital pass-through stopped\n");
215 sblive_writeptr(card
, (card
->is_audigy
? A_GPR_BASE
: GPR_BASE
) + pt
->enable_gpr
, 0, 0);
216 for (i
= 0; i
< 3; i
++) {
217 if (pt
->spcs_to_use
& (1 << i
))
218 sblive_writeptr(card
, SPCS0
+ i
, 0, pt
->old_spcs
[i
]);
220 pt
->state
= PT_STATE_INACTIVE
;
225 void emu10k1_pt_waveout_update(struct emu10k1_wavedevice
*wave_dev
)
227 struct woinst
*woinst
= wave_dev
->woinst
;
228 struct pt_data
*pt
= &wave_dev
->card
->pt
;
231 if (pt
->state
== PT_STATE_PLAYING
&& pt
->pos_gpr
>= 0) {
232 pos
= sblive_readptr(wave_dev
->card
, GPR_BASE
+ pt
->pos_gpr
, 0);
233 if (pos
> PT_BLOCKSAMPLES
)
234 pos
= PT_BLOCKSAMPLES
;
235 pos
= 4 * (PT_BLOCKSAMPLES
- pos
);
238 woinst
->total_played
= pt
->blocks_played
* woinst
->buffer
.fragment_size
+ pos
;
239 woinst
->buffer
.hw_pos
= pos
;