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 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_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 53static 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 61static 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 73static 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 81static 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 94static 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 102static 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 114static 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 122static 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 135static 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 151static 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 167static 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 187static 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 207static 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 259void 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(KERN_DEBUG "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 off = chip->pcm_hwptr + chip->pcm_tdone; 283 off %= chip->pcm_size; 284 chip->pcm_tdone += size; 285 while (size > 0) { 286 cont = chip->pcm_size - off; 287 if (cont > size) 288 cont = size; 289 pdacf_transfer(chip, cont, off); 290 off += cont; 291 off %= chip->pcm_size; 292 size -= cont; 293 } 294 spin_lock(&chip->reg_lock); 295 while (chip->pcm_tdone >= chip->pcm_period) { 296 chip->pcm_hwptr += chip->pcm_period; 297 chip->pcm_hwptr %= chip->pcm_size; 298 chip->pcm_tdone -= chip->pcm_period; 299 spin_unlock(&chip->reg_lock); 300 snd_pcm_period_elapsed(chip->pcm_substream); 301 spin_lock(&chip->reg_lock); 302 } 303 spin_unlock(&chip->reg_lock); 304 /* printk(KERN_DEBUG "TASKLET: end\n"); */ 305} 306