Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for Sound Core PDAudioCF soundcard | |
3 | * | |
c1017a4c | 4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | ||
1da177e4 LT |
21 | #include <sound/core.h> |
22 | #include "pdaudiocf.h" | |
23 | #include <sound/initval.h> | |
97432886 | 24 | #include <asm/irq_regs.h> |
1da177e4 LT |
25 | |
26 | /* | |
27 | * | |
28 | */ | |
7d12e780 | 29 | irqreturn_t pdacf_interrupt(int irq, void *dev) |
1da177e4 | 30 | { |
db131548 | 31 | struct snd_pdacf *chip = dev; |
1da177e4 LT |
32 | unsigned short stat; |
33 | ||
34 | if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| | |
35 | PDAUDIOCF_STAT_IS_CONFIGURED| | |
36 | PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED) | |
37 | return IRQ_HANDLED; /* IRQ_NONE here? */ | |
38 | ||
39 | stat = inw(chip->port + PDAUDIOCF_REG_ISR); | |
40 | if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) { | |
41 | if (stat & PDAUDIOCF_IRQOVR) /* should never happen */ | |
42 | snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n"); | |
43 | if (chip->pcm_substream) | |
1f04128a | 44 | tasklet_schedule(&chip->tq); |
1da177e4 LT |
45 | if (!(stat & PDAUDIOCF_IRQAKM)) |
46 | stat |= PDAUDIOCF_IRQAKM; /* check rate */ | |
47 | } | |
7d12e780 | 48 | if (get_irq_regs() != NULL) |
1da177e4 LT |
49 | snd_ak4117_check_rate_and_errors(chip->ak4117, 0); |
50 | return IRQ_HANDLED; | |
51 | } | |
52 | ||
53 | static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
54 | { | |
55 | while (size-- > 0) { | |
56 | *dst++ = inw(rdp_port) ^ xor; | |
57 | inw(rdp_port); | |
58 | } | |
59 | } | |
60 | ||
61 | static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
62 | { | |
63 | register u16 val1, val2; | |
64 | ||
65 | while (size-- > 0) { | |
66 | val1 = inw(rdp_port); | |
67 | val2 = inw(rdp_port); | |
68 | inw(rdp_port); | |
69 | *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | |
70 | } | |
71 | } | |
72 | ||
73 | static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
74 | { | |
75 | while (size-- > 0) { | |
76 | *dst++ = inw(rdp_port) ^ xor; | |
77 | *dst++ = inw(rdp_port) ^ xor; | |
78 | } | |
79 | } | |
80 | ||
81 | static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
82 | { | |
83 | register u16 val1, val2, val3; | |
84 | ||
85 | while (size-- > 0) { | |
86 | val1 = inw(rdp_port); | |
87 | val2 = inw(rdp_port); | |
88 | val3 = inw(rdp_port); | |
89 | *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | |
90 | *dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | |
91 | } | |
92 | } | |
93 | ||
94 | static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
95 | { | |
96 | while (size-- > 0) { | |
97 | *dst++ = swab16(inw(rdp_port) ^ xor); | |
98 | inw(rdp_port); | |
99 | } | |
100 | } | |
101 | ||
102 | static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
103 | { | |
104 | register u16 val1, val2; | |
105 | ||
106 | while (size-- > 0) { | |
107 | val1 = inw(rdp_port); | |
108 | val2 = inw(rdp_port); | |
109 | inw(rdp_port); | |
110 | *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); | |
111 | } | |
112 | } | |
113 | ||
114 | static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
115 | { | |
116 | while (size-- > 0) { | |
117 | *dst++ = swab16(inw(rdp_port) ^ xor); | |
118 | *dst++ = swab16(inw(rdp_port) ^ xor); | |
119 | } | |
120 | } | |
121 | ||
122 | static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
123 | { | |
124 | register u16 val1, val2, val3; | |
125 | ||
126 | while (size-- > 0) { | |
127 | val1 = inw(rdp_port); | |
128 | val2 = inw(rdp_port); | |
129 | val3 = inw(rdp_port); | |
130 | *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); | |
131 | *dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor); | |
132 | } | |
133 | } | |
134 | ||
135 | static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
136 | { | |
137 | register u16 val1, val2; | |
138 | register u32 xval1; | |
139 | ||
140 | while (size-- > 0) { | |
141 | val1 = inw(rdp_port); | |
142 | val2 = inw(rdp_port); | |
143 | inw(rdp_port); | |
144 | xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; | |
145 | *dst++ = (u8)(xval1 >> 8); | |
146 | *dst++ = (u8)(xval1 >> 16); | |
147 | *dst++ = (u8)(xval1 >> 24); | |
148 | } | |
149 | } | |
150 | ||
151 | static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | |
152 | { | |
153 | register u16 val1, val2; | |
154 | register u32 xval1; | |
155 | ||
156 | while (size-- > 0) { | |
157 | val1 = inw(rdp_port); | |
158 | val2 = inw(rdp_port); | |
159 | inw(rdp_port); | |
160 | xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; | |
161 | *dst++ = (u8)(xval1 >> 24); | |
162 | *dst++ = (u8)(xval1 >> 16); | |
163 | *dst++ = (u8)(xval1 >> 8); | |
164 | } | |
165 | } | |
166 | ||
167 | static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
168 | { | |
169 | register u16 val1, val2, val3; | |
170 | register u32 xval1, xval2; | |
171 | ||
172 | while (size-- > 0) { | |
173 | val1 = inw(rdp_port); | |
174 | val2 = inw(rdp_port); | |
175 | val3 = inw(rdp_port); | |
176 | xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | |
177 | xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | |
178 | *dst++ = (u8)(xval1 >> 8); | |
179 | *dst++ = (u8)(xval1 >> 16); | |
180 | *dst++ = (u8)(xval1 >> 24); | |
181 | *dst++ = (u8)(xval2 >> 8); | |
182 | *dst++ = (u8)(xval2 >> 16); | |
183 | *dst++ = (u8)(xval2 >> 24); | |
184 | } | |
185 | } | |
186 | ||
187 | static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | |
188 | { | |
189 | register u16 val1, val2, val3; | |
190 | register u32 xval1, xval2; | |
191 | ||
192 | while (size-- > 0) { | |
193 | val1 = inw(rdp_port); | |
194 | val2 = inw(rdp_port); | |
195 | val3 = inw(rdp_port); | |
196 | xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | |
197 | xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | |
198 | *dst++ = (u8)(xval1 >> 24); | |
199 | *dst++ = (u8)(xval1 >> 16); | |
200 | *dst++ = (u8)(xval1 >> 8); | |
201 | *dst++ = (u8)(xval2 >> 24); | |
202 | *dst++ = (u8)(xval2 >> 16); | |
203 | *dst++ = (u8)(xval2 >> 8); | |
204 | } | |
205 | } | |
206 | ||
db131548 | 207 | static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned int off) |
1da177e4 LT |
208 | { |
209 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | |
210 | unsigned int xor = chip->pcm_xor; | |
211 | ||
212 | if (chip->pcm_sample == 3) { | |
213 | if (chip->pcm_little) { | |
214 | if (chip->pcm_channels == 1) { | |
215 | pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); | |
216 | } else { | |
217 | pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); | |
218 | } | |
219 | } else { | |
220 | if (chip->pcm_channels == 1) { | |
221 | pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); | |
222 | } else { | |
223 | pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); | |
224 | } | |
225 | } | |
226 | return; | |
227 | } | |
228 | if (chip->pcm_swab == 0) { | |
229 | if (chip->pcm_channels == 1) { | |
230 | if (chip->pcm_frame == 2) { | |
231 | pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port); | |
232 | } else { | |
233 | pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port); | |
234 | } | |
235 | } else { | |
236 | if (chip->pcm_frame == 2) { | |
237 | pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | |
238 | } else { | |
239 | pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | |
240 | } | |
241 | } | |
242 | } else { | |
243 | if (chip->pcm_channels == 1) { | |
244 | if (chip->pcm_frame == 2) { | |
245 | pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port); | |
246 | } else { | |
247 | pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port); | |
248 | } | |
249 | } else { | |
250 | if (chip->pcm_frame == 2) { | |
251 | pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | |
252 | } else { | |
253 | pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | |
254 | } | |
255 | } | |
256 | } | |
257 | } | |
258 | ||
259 | void pdacf_tasklet(unsigned long private_data) | |
260 | { | |
db131548 | 261 | struct snd_pdacf *chip = (struct snd_pdacf *) private_data; |
1da177e4 LT |
262 | int size, off, cont, rdp, wdp; |
263 | ||
264 | if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) | |
265 | return; | |
266 | ||
267 | if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) | |
268 | return; | |
269 | ||
270 | rdp = inw(chip->port + PDAUDIOCF_REG_RDP); | |
271 | wdp = inw(chip->port + PDAUDIOCF_REG_WDP); | |
2ebfb8ee | 272 | /* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */ |
1da177e4 LT |
273 | size = wdp - rdp; |
274 | if (size < 0) | |
275 | size += 0x10000; | |
276 | if (size == 0) | |
277 | size = 0x10000; | |
278 | size /= chip->pcm_frame; | |
279 | if (size > 64) | |
280 | size -= 32; | |
281 | ||
282 | #if 0 | |
283 | chip->pcm_hwptr += size; | |
284 | chip->pcm_hwptr %= chip->pcm_size; | |
285 | chip->pcm_tdone += size; | |
286 | if (chip->pcm_frame == 2) { | |
287 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | |
288 | while (size-- > 0) { | |
289 | inw(rdp_port); | |
290 | inw(rdp_port); | |
291 | } | |
292 | } else { | |
293 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | |
294 | while (size-- > 0) { | |
295 | inw(rdp_port); | |
296 | inw(rdp_port); | |
297 | inw(rdp_port); | |
298 | } | |
299 | } | |
300 | #else | |
301 | off = chip->pcm_hwptr + chip->pcm_tdone; | |
302 | off %= chip->pcm_size; | |
303 | chip->pcm_tdone += size; | |
304 | while (size > 0) { | |
305 | cont = chip->pcm_size - off; | |
306 | if (cont > size) | |
307 | cont = size; | |
308 | pdacf_transfer(chip, cont, off); | |
309 | off += cont; | |
310 | off %= chip->pcm_size; | |
311 | size -= cont; | |
312 | } | |
313 | #endif | |
314 | spin_lock(&chip->reg_lock); | |
315 | while (chip->pcm_tdone >= chip->pcm_period) { | |
316 | chip->pcm_hwptr += chip->pcm_period; | |
317 | chip->pcm_hwptr %= chip->pcm_size; | |
318 | chip->pcm_tdone -= chip->pcm_period; | |
319 | spin_unlock(&chip->reg_lock); | |
320 | snd_pcm_period_elapsed(chip->pcm_substream); | |
321 | spin_lock(&chip->reg_lock); | |
322 | } | |
323 | spin_unlock(&chip->reg_lock); | |
2ebfb8ee | 324 | /* printk(KERN_DEBUG "TASKLET: end\n"); */ |
1da177e4 | 325 | } |