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