1 /*
2  * Driver for Sound Core PDAudioCF soundcard
3  *
4  * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.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/core.h>
22 #include "pdaudiocf.h"
23 #include <sound/initval.h>
24 #include <asm/irq_regs.h>
25 
26 /*
27  *
28  */
29 irqreturn_t pdacf_interrupt(int irq, void *dev)
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 (get_irq_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 }
326