313 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Sound Core PDAudioCF soundcard4 *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 dev_err(chip->card->dev, "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 size = wdp - rdp;261 if (size < 0)262 size += 0x10000;263 if (size == 0)264 size = 0x10000;265 size /= chip->pcm_frame;266 if (size > 64)267 size -= 32;268 269#if 0270 chip->pcm_hwptr += size;271 chip->pcm_hwptr %= chip->pcm_size;272 chip->pcm_tdone += size;273 if (chip->pcm_frame == 2) {274 unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;275 while (size-- > 0) {276 inw(rdp_port);277 inw(rdp_port);278 }279 } else {280 unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;281 while (size-- > 0) {282 inw(rdp_port);283 inw(rdp_port);284 inw(rdp_port);285 }286 }287#else288 off = chip->pcm_hwptr + chip->pcm_tdone;289 off %= chip->pcm_size;290 chip->pcm_tdone += size;291 while (size > 0) {292 cont = chip->pcm_size - off;293 if (cont > size)294 cont = size;295 pdacf_transfer(chip, cont, off);296 off += cont;297 off %= chip->pcm_size;298 size -= cont;299 }300#endif301 mutex_lock(&chip->reg_lock);302 while (chip->pcm_tdone >= chip->pcm_period) {303 chip->pcm_hwptr += chip->pcm_period;304 chip->pcm_hwptr %= chip->pcm_size;305 chip->pcm_tdone -= chip->pcm_period;306 mutex_unlock(&chip->reg_lock);307 snd_pcm_period_elapsed(chip->pcm_substream);308 mutex_lock(&chip->reg_lock);309 }310 mutex_unlock(&chip->reg_lock);311 return IRQ_HANDLED;312}313