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 */ 16 irqreturn_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 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 195 static 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 247 irqreturn_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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.