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