1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Driver for Sound Core PDAudioCF soundcard
4 *
5 * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
6 */
7
8#include <sound/core.h>
9#include "pdaudiocf.h"
10#include <sound/initval.h>
11#include <asm/irq_regs.h>
12
13/*
14 *
15 */
16irqreturn_t pdacf_interrupt(int irq, void *dev)
17{
18	struct snd_pdacf *chip = dev;
19	unsigned short stat;
20	bool wake_thread = false;
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)
32			wake_thread = true;
33		if (!(stat & PDAUDIOCF_IRQAKM))
34			stat |= PDAUDIOCF_IRQAKM;	/* check rate */
35	}
36	if (get_irq_regs() != NULL)
37		snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
38	return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
39}
40
41static 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
49static 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
61static 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
69static 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
82static 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
90static 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
102static 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
110static 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
123static 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
139static 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
155static 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
175static 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
195static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned int off)
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
247irqreturn_t pdacf_threaded_irq(int irq, void *dev)
248{
249	struct snd_pdacf *chip = dev;
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)
253		return IRQ_HANDLED;
254
255	if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
256		return IRQ_HANDLED;
257
258	rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
259	wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
260	/* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */
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
302	mutex_lock(&chip->reg_lock);
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;
307		mutex_unlock(&chip->reg_lock);
308		snd_pcm_period_elapsed(chip->pcm_substream);
309		mutex_lock(&chip->reg_lock);
310	}
311	mutex_unlock(&chip->reg_lock);
312	return IRQ_HANDLED;
313}
314