Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / sound / pcmcia / pdaudiocf / pdaudiocf_irq.c
1 /*
2  * Driver for Sound Core PDAudioCF soundcard
3  *
4  * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
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
21 #include <sound/driver.h>
22 #include <sound/core.h>
23 #include "pdaudiocf.h"
24 #include <sound/initval.h>
25
26 /*
27  *
28  */
29 irqreturn_t pdacf_interrupt(int irq, void *dev, struct pt_regs *regs)
30 {
31         struct snd_pdacf *chip = dev;
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)
44                         tasklet_hi_schedule(&chip->tq);
45                 if (!(stat & PDAUDIOCF_IRQAKM))
46                         stat |= PDAUDIOCF_IRQAKM;       /* check rate */
47         }
48         if (regs != NULL)
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
207 static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned int off)
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 {
261         struct snd_pdacf *chip = (struct snd_pdacf *) private_data;
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);
272         // printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp);
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);
324         // printk("TASKLET: end\n");
325 }