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 */ 29irqreturn_t pdacf_interrupt(int irq, void *dev) 30{ 31 struct snd_pdacf *chip = dev; 32 unsigned short stat; 33 bool wake_thread = false; 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 wake_thread = true; 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 wake_thread ? IRQ_WAKE_THREAD : 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 260irqreturn_t pdacf_threaded_irq(int irq, void *dev) 261{ 262 struct snd_pdacf *chip = dev; 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 IRQ_HANDLED; 267 268 if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) 269 return IRQ_HANDLED; 270 271 rdp = inw(chip->port + PDAUDIOCF_REG_RDP); 272 wdp = inw(chip->port + PDAUDIOCF_REG_WDP); 273 /* printk(KERN_DEBUG "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#if 0 284 chip->pcm_hwptr += size; 285 chip->pcm_hwptr %= chip->pcm_size; 286 chip->pcm_tdone += size; 287 if (chip->pcm_frame == 2) { 288 unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; 289 while (size-- > 0) { 290 inw(rdp_port); 291 inw(rdp_port); 292 } 293 } else { 294 unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; 295 while (size-- > 0) { 296 inw(rdp_port); 297 inw(rdp_port); 298 inw(rdp_port); 299 } 300 } 301#else 302 off = chip->pcm_hwptr + chip->pcm_tdone; 303 off %= chip->pcm_size; 304 chip->pcm_tdone += size; 305 while (size > 0) { 306 cont = chip->pcm_size - off; 307 if (cont > size) 308 cont = size; 309 pdacf_transfer(chip, cont, off); 310 off += cont; 311 off %= chip->pcm_size; 312 size -= cont; 313 } 314#endif 315 mutex_lock(&chip->reg_lock); 316 while (chip->pcm_tdone >= chip->pcm_period) { 317 chip->pcm_hwptr += chip->pcm_period; 318 chip->pcm_hwptr %= chip->pcm_size; 319 chip->pcm_tdone -= chip->pcm_period; 320 mutex_unlock(&chip->reg_lock); 321 snd_pcm_period_elapsed(chip->pcm_substream); 322 mutex_lock(&chip->reg_lock); 323 } 324 mutex_unlock(&chip->reg_lock); 325 return IRQ_HANDLED; 326} 327