1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Driver for Sound Core PDAudioCF soundcard
41da177e4SLinus Torvalds *
5c1017a4cSJaroslav Kysela * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #include <sound/core.h>
91da177e4SLinus Torvalds #include "pdaudiocf.h"
101da177e4SLinus Torvalds #include <sound/initval.h>
1197432886SAl Viro #include <asm/irq_regs.h>
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds /*
141da177e4SLinus Torvalds *
151da177e4SLinus Torvalds */
pdacf_interrupt(int irq,void * dev)167d12e780SDavid Howells irqreturn_t pdacf_interrupt(int irq, void *dev)
171da177e4SLinus Torvalds {
18db131548STakashi Iwai struct snd_pdacf *chip = dev;
191da177e4SLinus Torvalds unsigned short stat;
203b73cfe5STakashi Iwai bool wake_thread = false;
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|
231da177e4SLinus Torvalds PDAUDIOCF_STAT_IS_CONFIGURED|
241da177e4SLinus Torvalds PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
251da177e4SLinus Torvalds return IRQ_HANDLED; /* IRQ_NONE here? */
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds stat = inw(chip->port + PDAUDIOCF_REG_ISR);
281da177e4SLinus Torvalds if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) {
291da177e4SLinus Torvalds if (stat & PDAUDIOCF_IRQOVR) /* should never happen */
301da177e4SLinus Torvalds snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
311da177e4SLinus Torvalds if (chip->pcm_substream)
323b73cfe5STakashi Iwai wake_thread = true;
331da177e4SLinus Torvalds if (!(stat & PDAUDIOCF_IRQAKM))
341da177e4SLinus Torvalds stat |= PDAUDIOCF_IRQAKM; /* check rate */
351da177e4SLinus Torvalds }
367d12e780SDavid Howells if (get_irq_regs() != NULL)
371da177e4SLinus Torvalds snd_ak4117_check_rate_and_errors(chip->ak4117, 0);
383b73cfe5STakashi Iwai return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds
pdacf_transfer_mono16(u16 * dst,u16 xor,unsigned int size,unsigned long rdp_port)411da177e4SLinus Torvalds static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds while (size-- > 0) {
441da177e4SLinus Torvalds *dst++ = inw(rdp_port) ^ xor;
451da177e4SLinus Torvalds inw(rdp_port);
461da177e4SLinus Torvalds }
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds
pdacf_transfer_mono32(u32 * dst,u32 xor,unsigned int size,unsigned long rdp_port)491da177e4SLinus Torvalds static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds register u16 val1, val2;
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds while (size-- > 0) {
541da177e4SLinus Torvalds val1 = inw(rdp_port);
551da177e4SLinus Torvalds val2 = inw(rdp_port);
561da177e4SLinus Torvalds inw(rdp_port);
571da177e4SLinus Torvalds *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds
pdacf_transfer_stereo16(u16 * dst,u16 xor,unsigned int size,unsigned long rdp_port)611da177e4SLinus Torvalds static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds while (size-- > 0) {
641da177e4SLinus Torvalds *dst++ = inw(rdp_port) ^ xor;
651da177e4SLinus Torvalds *dst++ = inw(rdp_port) ^ xor;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds
pdacf_transfer_stereo32(u32 * dst,u32 xor,unsigned int size,unsigned long rdp_port)691da177e4SLinus Torvalds static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds register u16 val1, val2, val3;
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds while (size-- > 0) {
741da177e4SLinus Torvalds val1 = inw(rdp_port);
751da177e4SLinus Torvalds val2 = inw(rdp_port);
761da177e4SLinus Torvalds val3 = inw(rdp_port);
771da177e4SLinus Torvalds *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
781da177e4SLinus Torvalds *dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds
pdacf_transfer_mono16sw(u16 * dst,u16 xor,unsigned int size,unsigned long rdp_port)821da177e4SLinus Torvalds static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds while (size-- > 0) {
851da177e4SLinus Torvalds *dst++ = swab16(inw(rdp_port) ^ xor);
861da177e4SLinus Torvalds inw(rdp_port);
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds
pdacf_transfer_mono32sw(u32 * dst,u32 xor,unsigned int size,unsigned long rdp_port)901da177e4SLinus Torvalds static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds register u16 val1, val2;
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds while (size-- > 0) {
951da177e4SLinus Torvalds val1 = inw(rdp_port);
961da177e4SLinus Torvalds val2 = inw(rdp_port);
971da177e4SLinus Torvalds inw(rdp_port);
981da177e4SLinus Torvalds *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds
pdacf_transfer_stereo16sw(u16 * dst,u16 xor,unsigned int size,unsigned long rdp_port)1021da177e4SLinus Torvalds static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1031da177e4SLinus Torvalds {
1041da177e4SLinus Torvalds while (size-- > 0) {
1051da177e4SLinus Torvalds *dst++ = swab16(inw(rdp_port) ^ xor);
1061da177e4SLinus Torvalds *dst++ = swab16(inw(rdp_port) ^ xor);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds
pdacf_transfer_stereo32sw(u32 * dst,u32 xor,unsigned int size,unsigned long rdp_port)1101da177e4SLinus Torvalds static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds register u16 val1, val2, val3;
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds while (size-- > 0) {
1151da177e4SLinus Torvalds val1 = inw(rdp_port);
1161da177e4SLinus Torvalds val2 = inw(rdp_port);
1171da177e4SLinus Torvalds val3 = inw(rdp_port);
1181da177e4SLinus Torvalds *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor);
1191da177e4SLinus Torvalds *dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds
pdacf_transfer_mono24le(u8 * dst,u16 xor,unsigned int size,unsigned long rdp_port)1231da177e4SLinus Torvalds static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds register u16 val1, val2;
1261da177e4SLinus Torvalds register u32 xval1;
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds while (size-- > 0) {
1291da177e4SLinus Torvalds val1 = inw(rdp_port);
1301da177e4SLinus Torvalds val2 = inw(rdp_port);
1311da177e4SLinus Torvalds inw(rdp_port);
1321da177e4SLinus Torvalds xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
1331da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 8);
1341da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 16);
1351da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 24);
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds
pdacf_transfer_mono24be(u8 * dst,u16 xor,unsigned int size,unsigned long rdp_port)1391da177e4SLinus Torvalds static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds register u16 val1, val2;
1421da177e4SLinus Torvalds register u32 xval1;
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds while (size-- > 0) {
1451da177e4SLinus Torvalds val1 = inw(rdp_port);
1461da177e4SLinus Torvalds val2 = inw(rdp_port);
1471da177e4SLinus Torvalds inw(rdp_port);
1481da177e4SLinus Torvalds xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor;
1491da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 24);
1501da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 16);
1511da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 8);
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds
pdacf_transfer_stereo24le(u8 * dst,u32 xor,unsigned int size,unsigned long rdp_port)1551da177e4SLinus Torvalds static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds register u16 val1, val2, val3;
1581da177e4SLinus Torvalds register u32 xval1, xval2;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds while (size-- > 0) {
1611da177e4SLinus Torvalds val1 = inw(rdp_port);
1621da177e4SLinus Torvalds val2 = inw(rdp_port);
1631da177e4SLinus Torvalds val3 = inw(rdp_port);
1641da177e4SLinus Torvalds xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
1651da177e4SLinus Torvalds xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
1661da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 8);
1671da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 16);
1681da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 24);
1691da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 8);
1701da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 16);
1711da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 24);
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds
pdacf_transfer_stereo24be(u8 * dst,u32 xor,unsigned int size,unsigned long rdp_port)1751da177e4SLinus Torvalds static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds register u16 val1, val2, val3;
1781da177e4SLinus Torvalds register u32 xval1, xval2;
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds while (size-- > 0) {
1811da177e4SLinus Torvalds val1 = inw(rdp_port);
1821da177e4SLinus Torvalds val2 = inw(rdp_port);
1831da177e4SLinus Torvalds val3 = inw(rdp_port);
1841da177e4SLinus Torvalds xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor;
1851da177e4SLinus Torvalds xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor;
1861da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 24);
1871da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 16);
1881da177e4SLinus Torvalds *dst++ = (u8)(xval1 >> 8);
1891da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 24);
1901da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 16);
1911da177e4SLinus Torvalds *dst++ = (u8)(xval2 >> 8);
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds
pdacf_transfer(struct snd_pdacf * chip,unsigned int size,unsigned int off)195db131548STakashi Iwai static void pdacf_transfer(struct snd_pdacf *chip, unsigned int size, unsigned int off)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
1981da177e4SLinus Torvalds unsigned int xor = chip->pcm_xor;
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds if (chip->pcm_sample == 3) {
2011da177e4SLinus Torvalds if (chip->pcm_little) {
2021da177e4SLinus Torvalds if (chip->pcm_channels == 1) {
2031da177e4SLinus Torvalds pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
2041da177e4SLinus Torvalds } else {
2051da177e4SLinus Torvalds pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds } else {
2081da177e4SLinus Torvalds if (chip->pcm_channels == 1) {
2091da177e4SLinus Torvalds pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port);
2101da177e4SLinus Torvalds } else {
2111da177e4SLinus Torvalds pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds return;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds if (chip->pcm_swab == 0) {
2171da177e4SLinus Torvalds if (chip->pcm_channels == 1) {
2181da177e4SLinus Torvalds if (chip->pcm_frame == 2) {
2191da177e4SLinus Torvalds pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port);
2201da177e4SLinus Torvalds } else {
2211da177e4SLinus Torvalds pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds } else {
2241da177e4SLinus Torvalds if (chip->pcm_frame == 2) {
2251da177e4SLinus Torvalds pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2261da177e4SLinus Torvalds } else {
2271da177e4SLinus Torvalds pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds } else {
2311da177e4SLinus Torvalds if (chip->pcm_channels == 1) {
2321da177e4SLinus Torvalds if (chip->pcm_frame == 2) {
2331da177e4SLinus Torvalds pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port);
2341da177e4SLinus Torvalds } else {
2351da177e4SLinus Torvalds pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port);
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds } else {
2381da177e4SLinus Torvalds if (chip->pcm_frame == 2) {
2391da177e4SLinus Torvalds pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2401da177e4SLinus Torvalds } else {
2411da177e4SLinus Torvalds pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port);
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds
pdacf_threaded_irq(int irq,void * dev)2473b73cfe5STakashi Iwai irqreturn_t pdacf_threaded_irq(int irq, void *dev)
2481da177e4SLinus Torvalds {
2493b73cfe5STakashi Iwai struct snd_pdacf *chip = dev;
2501da177e4SLinus Torvalds int size, off, cont, rdp, wdp;
2511da177e4SLinus Torvalds
2521da177e4SLinus Torvalds if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED)
2533b73cfe5STakashi Iwai return IRQ_HANDLED;
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream))
2563b73cfe5STakashi Iwai return IRQ_HANDLED;
2571da177e4SLinus Torvalds
2581da177e4SLinus Torvalds rdp = inw(chip->port + PDAUDIOCF_REG_RDP);
2591da177e4SLinus Torvalds wdp = inw(chip->port + PDAUDIOCF_REG_WDP);
2602ebfb8eeSTakashi Iwai /* printk(KERN_DEBUG "TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); */
2611da177e4SLinus Torvalds size = wdp - rdp;
2621da177e4SLinus Torvalds if (size < 0)
2631da177e4SLinus Torvalds size += 0x10000;
2641da177e4SLinus Torvalds if (size == 0)
2651da177e4SLinus Torvalds size = 0x10000;
2661da177e4SLinus Torvalds size /= chip->pcm_frame;
2671da177e4SLinus Torvalds if (size > 64)
2681da177e4SLinus Torvalds size -= 32;
2691da177e4SLinus Torvalds
2701da177e4SLinus Torvalds #if 0
2711da177e4SLinus Torvalds chip->pcm_hwptr += size;
2721da177e4SLinus Torvalds chip->pcm_hwptr %= chip->pcm_size;
2731da177e4SLinus Torvalds chip->pcm_tdone += size;
2741da177e4SLinus Torvalds if (chip->pcm_frame == 2) {
2751da177e4SLinus Torvalds unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
2761da177e4SLinus Torvalds while (size-- > 0) {
2771da177e4SLinus Torvalds inw(rdp_port);
2781da177e4SLinus Torvalds inw(rdp_port);
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds } else {
2811da177e4SLinus Torvalds unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD;
2821da177e4SLinus Torvalds while (size-- > 0) {
2831da177e4SLinus Torvalds inw(rdp_port);
2841da177e4SLinus Torvalds inw(rdp_port);
2851da177e4SLinus Torvalds inw(rdp_port);
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds #else
2891da177e4SLinus Torvalds off = chip->pcm_hwptr + chip->pcm_tdone;
2901da177e4SLinus Torvalds off %= chip->pcm_size;
2911da177e4SLinus Torvalds chip->pcm_tdone += size;
2921da177e4SLinus Torvalds while (size > 0) {
2931da177e4SLinus Torvalds cont = chip->pcm_size - off;
2941da177e4SLinus Torvalds if (cont > size)
2951da177e4SLinus Torvalds cont = size;
2961da177e4SLinus Torvalds pdacf_transfer(chip, cont, off);
2971da177e4SLinus Torvalds off += cont;
2981da177e4SLinus Torvalds off %= chip->pcm_size;
2991da177e4SLinus Torvalds size -= cont;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds #endif
3023b73cfe5STakashi Iwai mutex_lock(&chip->reg_lock);
3031da177e4SLinus Torvalds while (chip->pcm_tdone >= chip->pcm_period) {
3041da177e4SLinus Torvalds chip->pcm_hwptr += chip->pcm_period;
3051da177e4SLinus Torvalds chip->pcm_hwptr %= chip->pcm_size;
3061da177e4SLinus Torvalds chip->pcm_tdone -= chip->pcm_period;
3073b73cfe5STakashi Iwai mutex_unlock(&chip->reg_lock);
3081da177e4SLinus Torvalds snd_pcm_period_elapsed(chip->pcm_substream);
3093b73cfe5STakashi Iwai mutex_lock(&chip->reg_lock);
3101da177e4SLinus Torvalds }
3113b73cfe5STakashi Iwai mutex_unlock(&chip->reg_lock);
3123b73cfe5STakashi Iwai return IRQ_HANDLED;
3131da177e4SLinus Torvalds }
314