1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/sound/oss/dmasound/dmasound_q40.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Q40 DMA Sound Driver
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
81da177e4SLinus Torvalds * prior to 28/01/2001
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * 28/01/2001 [0.1] Iain Sandoe
111da177e4SLinus Torvalds * - added versioning
121da177e4SLinus Torvalds * - put in and populated the hardware_afmts field.
131da177e4SLinus Torvalds * [0.2] - put in SNDCTL_DSP_GETCAPS value.
141da177e4SLinus Torvalds * [0.3] - put in default hard/soft settings.
151da177e4SLinus Torvalds */
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/init.h>
201da177e4SLinus Torvalds #include <linux/slab.h>
211da177e4SLinus Torvalds #include <linux/soundcard.h>
221da177e4SLinus Torvalds #include <linux/interrupt.h>
231da177e4SLinus Torvalds
247c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
251da177e4SLinus Torvalds #include <asm/q40ints.h>
261da177e4SLinus Torvalds #include <asm/q40_master.h>
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds #include "dmasound.h"
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds #define DMASOUND_Q40_REVISION 0
311da177e4SLinus Torvalds #define DMASOUND_Q40_EDITION 3
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds static int expand_bal; /* Balance factor for expanding (not volume!) */
341da177e4SLinus Torvalds static int expand_data; /* Data for expanding */
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds /*** Low level stuff *********************************************************/
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds
401ef64e67SAl Viro static void *Q40Alloc(unsigned int size, gfp_t flags);
411da177e4SLinus Torvalds static void Q40Free(void *, unsigned int);
421da177e4SLinus Torvalds static int Q40IrqInit(void);
431da177e4SLinus Torvalds #ifdef MODULE
441da177e4SLinus Torvalds static void Q40IrqCleanUp(void);
451da177e4SLinus Torvalds #endif
461da177e4SLinus Torvalds static void Q40Silence(void);
471da177e4SLinus Torvalds static void Q40Init(void);
481da177e4SLinus Torvalds static int Q40SetFormat(int format);
491da177e4SLinus Torvalds static int Q40SetVolume(int volume);
501da177e4SLinus Torvalds static void Q40PlayNextFrame(int index);
511da177e4SLinus Torvalds static void Q40Play(void);
527d12e780SDavid Howells static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
537d12e780SDavid Howells static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
541da177e4SLinus Torvalds static void Q40Interrupt(void);
551da177e4SLinus Torvalds
561da177e4SLinus Torvalds
571da177e4SLinus Torvalds /*** Mid level stuff *********************************************************/
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds /* userCount, frameUsed, frameLeft == byte counts */
q40_ct_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)62031eb4cdSAl Viro static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
631da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
641da177e4SLinus Torvalds ssize_t frameLeft)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
671da177e4SLinus Torvalds ssize_t count, used;
681da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds used = count = min_t(size_t, userCount, frameLeft);
711da177e4SLinus Torvalds if (copy_from_user(p,userPtr,count))
721da177e4SLinus Torvalds return -EFAULT;
731da177e4SLinus Torvalds while (count > 0) {
741da177e4SLinus Torvalds *p = table[*p]+128;
751da177e4SLinus Torvalds p++;
761da177e4SLinus Torvalds count--;
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds *frameUsed += used ;
791da177e4SLinus Torvalds return used;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds
q40_ct_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)83031eb4cdSAl Viro static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
841da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
851da177e4SLinus Torvalds ssize_t frameLeft)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds ssize_t count, used;
881da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds used = count = min_t(size_t, userCount, frameLeft);
911da177e4SLinus Torvalds if (copy_from_user(p,userPtr,count))
921da177e4SLinus Torvalds return -EFAULT;
931da177e4SLinus Torvalds while (count > 0) {
941da177e4SLinus Torvalds *p = *p + 128;
951da177e4SLinus Torvalds p++;
961da177e4SLinus Torvalds count--;
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds *frameUsed += used;
991da177e4SLinus Torvalds return used;
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds
q40_ct_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)102031eb4cdSAl Viro static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
1031da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
1041da177e4SLinus Torvalds ssize_t frameLeft)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds ssize_t count, used;
1071da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds used = count = min_t(size_t, userCount, frameLeft);
1101da177e4SLinus Torvalds if (copy_from_user(p,userPtr,count))
1111da177e4SLinus Torvalds return -EFAULT;
1121da177e4SLinus Torvalds *frameUsed += used;
1131da177e4SLinus Torvalds return used;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds /* a bit too complicated to optimise right now ..*/
q40_ctx_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)118031eb4cdSAl Viro static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
1191da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
1201da177e4SLinus Torvalds ssize_t frameLeft)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds unsigned char *table = (unsigned char *)
1231da177e4SLinus Torvalds (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
1241da177e4SLinus Torvalds unsigned int data = expand_data;
1251da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
1261da177e4SLinus Torvalds int bal = expand_bal;
1271da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
1281da177e4SLinus Torvalds int utotal, ftotal;
1291da177e4SLinus Torvalds
1301da177e4SLinus Torvalds ftotal = frameLeft;
1311da177e4SLinus Torvalds utotal = userCount;
1321da177e4SLinus Torvalds while (frameLeft) {
1331da177e4SLinus Torvalds u_char c;
1341da177e4SLinus Torvalds if (bal < 0) {
1351da177e4SLinus Torvalds if (userCount == 0)
1361da177e4SLinus Torvalds break;
1371da177e4SLinus Torvalds if (get_user(c, userPtr++))
1381da177e4SLinus Torvalds return -EFAULT;
1391da177e4SLinus Torvalds data = table[c];
1401da177e4SLinus Torvalds data += 0x80;
1411da177e4SLinus Torvalds userCount--;
1421da177e4SLinus Torvalds bal += hSpeed;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds *p++ = data;
1451da177e4SLinus Torvalds frameLeft--;
1461da177e4SLinus Torvalds bal -= sSpeed;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds expand_bal = bal;
1491da177e4SLinus Torvalds expand_data = data;
1501da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft);
1511da177e4SLinus Torvalds utotal -= userCount;
1521da177e4SLinus Torvalds return utotal;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds
1551da177e4SLinus Torvalds
q40_ctx_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)156031eb4cdSAl Viro static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
1571da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
1581da177e4SLinus Torvalds ssize_t frameLeft)
1591da177e4SLinus Torvalds {
1601da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
1611da177e4SLinus Torvalds unsigned int data = expand_data;
1621da177e4SLinus Torvalds int bal = expand_bal;
1631da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
1641da177e4SLinus Torvalds int utotal, ftotal;
1651da177e4SLinus Torvalds
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds ftotal = frameLeft;
1681da177e4SLinus Torvalds utotal = userCount;
1691da177e4SLinus Torvalds while (frameLeft) {
1701da177e4SLinus Torvalds u_char c;
1711da177e4SLinus Torvalds if (bal < 0) {
1721da177e4SLinus Torvalds if (userCount == 0)
1731da177e4SLinus Torvalds break;
1741da177e4SLinus Torvalds if (get_user(c, userPtr++))
1751da177e4SLinus Torvalds return -EFAULT;
1761da177e4SLinus Torvalds data = c ;
1771da177e4SLinus Torvalds data += 0x80;
1781da177e4SLinus Torvalds userCount--;
1791da177e4SLinus Torvalds bal += hSpeed;
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds *p++ = data;
1821da177e4SLinus Torvalds frameLeft--;
1831da177e4SLinus Torvalds bal -= sSpeed;
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds expand_bal = bal;
1861da177e4SLinus Torvalds expand_data = data;
1871da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft);
1881da177e4SLinus Torvalds utotal -= userCount;
1891da177e4SLinus Torvalds return utotal;
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds
1921da177e4SLinus Torvalds
q40_ctx_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)193031eb4cdSAl Viro static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
1941da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
1951da177e4SLinus Torvalds ssize_t frameLeft)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
1981da177e4SLinus Torvalds unsigned int data = expand_data;
1991da177e4SLinus Torvalds int bal = expand_bal;
2001da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
2011da177e4SLinus Torvalds int utotal, ftotal;
2021da177e4SLinus Torvalds
2031da177e4SLinus Torvalds ftotal = frameLeft;
2041da177e4SLinus Torvalds utotal = userCount;
2051da177e4SLinus Torvalds while (frameLeft) {
2061da177e4SLinus Torvalds u_char c;
2071da177e4SLinus Torvalds if (bal < 0) {
2081da177e4SLinus Torvalds if (userCount == 0)
2091da177e4SLinus Torvalds break;
2101da177e4SLinus Torvalds if (get_user(c, userPtr++))
2111da177e4SLinus Torvalds return -EFAULT;
2121da177e4SLinus Torvalds data = c ;
2131da177e4SLinus Torvalds userCount--;
2141da177e4SLinus Torvalds bal += hSpeed;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds *p++ = data;
2171da177e4SLinus Torvalds frameLeft--;
2181da177e4SLinus Torvalds bal -= sSpeed;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds expand_bal = bal;
2211da177e4SLinus Torvalds expand_data = data;
2221da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft) ;
2231da177e4SLinus Torvalds utotal -= userCount;
2241da177e4SLinus Torvalds return utotal;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds
2271da177e4SLinus Torvalds /* compressing versions */
q40_ctc_law(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)228031eb4cdSAl Viro static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
2291da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
2301da177e4SLinus Torvalds ssize_t frameLeft)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds unsigned char *table = (unsigned char *)
2331da177e4SLinus Torvalds (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
2341da177e4SLinus Torvalds unsigned int data = expand_data;
2351da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
2361da177e4SLinus Torvalds int bal = expand_bal;
2371da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
2381da177e4SLinus Torvalds int utotal, ftotal;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds ftotal = frameLeft;
2411da177e4SLinus Torvalds utotal = userCount;
2421da177e4SLinus Torvalds while (frameLeft) {
2431da177e4SLinus Torvalds u_char c;
2441da177e4SLinus Torvalds while(bal<0) {
2451da177e4SLinus Torvalds if (userCount == 0)
2461da177e4SLinus Torvalds goto lout;
2471da177e4SLinus Torvalds if (!(bal<(-hSpeed))) {
2481da177e4SLinus Torvalds if (get_user(c, userPtr))
2491da177e4SLinus Torvalds return -EFAULT;
2501da177e4SLinus Torvalds data = 0x80 + table[c];
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds userPtr++;
2531da177e4SLinus Torvalds userCount--;
2541da177e4SLinus Torvalds bal += hSpeed;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds *p++ = data;
2571da177e4SLinus Torvalds frameLeft--;
2581da177e4SLinus Torvalds bal -= sSpeed;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds lout:
2611da177e4SLinus Torvalds expand_bal = bal;
2621da177e4SLinus Torvalds expand_data = data;
2631da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft);
2641da177e4SLinus Torvalds utotal -= userCount;
2651da177e4SLinus Torvalds return utotal;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds
2681da177e4SLinus Torvalds
q40_ctc_s8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)269031eb4cdSAl Viro static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
2701da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
2711da177e4SLinus Torvalds ssize_t frameLeft)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
2741da177e4SLinus Torvalds unsigned int data = expand_data;
2751da177e4SLinus Torvalds int bal = expand_bal;
2761da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
2771da177e4SLinus Torvalds int utotal, ftotal;
2781da177e4SLinus Torvalds
2791da177e4SLinus Torvalds ftotal = frameLeft;
2801da177e4SLinus Torvalds utotal = userCount;
2811da177e4SLinus Torvalds while (frameLeft) {
2821da177e4SLinus Torvalds u_char c;
2831da177e4SLinus Torvalds while (bal < 0) {
2841da177e4SLinus Torvalds if (userCount == 0)
2851da177e4SLinus Torvalds goto lout;
2861da177e4SLinus Torvalds if (!(bal<(-hSpeed))) {
2871da177e4SLinus Torvalds if (get_user(c, userPtr))
2881da177e4SLinus Torvalds return -EFAULT;
2891da177e4SLinus Torvalds data = c + 0x80;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds userPtr++;
2921da177e4SLinus Torvalds userCount--;
2931da177e4SLinus Torvalds bal += hSpeed;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds *p++ = data;
2961da177e4SLinus Torvalds frameLeft--;
2971da177e4SLinus Torvalds bal -= sSpeed;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds lout:
3001da177e4SLinus Torvalds expand_bal = bal;
3011da177e4SLinus Torvalds expand_data = data;
3021da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft);
3031da177e4SLinus Torvalds utotal -= userCount;
3041da177e4SLinus Torvalds return utotal;
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvalds
q40_ctc_u8(const u_char __user * userPtr,size_t userCount,u_char frame[],ssize_t * frameUsed,ssize_t frameLeft)308031eb4cdSAl Viro static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
3091da177e4SLinus Torvalds u_char frame[], ssize_t *frameUsed,
3101da177e4SLinus Torvalds ssize_t frameLeft)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds u_char *p = (u_char *) &frame[*frameUsed];
3131da177e4SLinus Torvalds unsigned int data = expand_data;
3141da177e4SLinus Torvalds int bal = expand_bal;
3151da177e4SLinus Torvalds int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
3161da177e4SLinus Torvalds int utotal, ftotal;
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds ftotal = frameLeft;
3191da177e4SLinus Torvalds utotal = userCount;
3201da177e4SLinus Torvalds while (frameLeft) {
3211da177e4SLinus Torvalds u_char c;
3221da177e4SLinus Torvalds while (bal < 0) {
3231da177e4SLinus Torvalds if (userCount == 0)
3241da177e4SLinus Torvalds goto lout;
3251da177e4SLinus Torvalds if (!(bal<(-hSpeed))) {
3261da177e4SLinus Torvalds if (get_user(c, userPtr))
3271da177e4SLinus Torvalds return -EFAULT;
3281da177e4SLinus Torvalds data = c ;
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds userPtr++;
3311da177e4SLinus Torvalds userCount--;
3321da177e4SLinus Torvalds bal += hSpeed;
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds *p++ = data;
3351da177e4SLinus Torvalds frameLeft--;
3361da177e4SLinus Torvalds bal -= sSpeed;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds lout:
3391da177e4SLinus Torvalds expand_bal = bal;
3401da177e4SLinus Torvalds expand_data = data;
3411da177e4SLinus Torvalds *frameUsed += (ftotal - frameLeft) ;
3421da177e4SLinus Torvalds utotal -= userCount;
3431da177e4SLinus Torvalds return utotal;
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds
3461da177e4SLinus Torvalds
3471da177e4SLinus Torvalds static TRANS transQ40Normal = {
3481da177e4SLinus Torvalds q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
3491da177e4SLinus Torvalds };
3501da177e4SLinus Torvalds
3511da177e4SLinus Torvalds static TRANS transQ40Expanding = {
3521da177e4SLinus Torvalds q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
3531da177e4SLinus Torvalds };
3541da177e4SLinus Torvalds
3551da177e4SLinus Torvalds static TRANS transQ40Compressing = {
3561da177e4SLinus Torvalds q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
3571da177e4SLinus Torvalds };
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds /*** Low level stuff *********************************************************/
3611da177e4SLinus Torvalds
Q40Alloc(unsigned int size,gfp_t flags)3621ef64e67SAl Viro static void *Q40Alloc(unsigned int size, gfp_t flags)
3631da177e4SLinus Torvalds {
3641da177e4SLinus Torvalds return kmalloc(size, flags); /* change to vmalloc */
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds
Q40Free(void * ptr,unsigned int size)3671da177e4SLinus Torvalds static void Q40Free(void *ptr, unsigned int size)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds kfree(ptr);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds
Q40IrqInit(void)3721da177e4SLinus Torvalds static int __init Q40IrqInit(void)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds /* Register interrupt handler. */
37589bde7b8SGeert Uytterhoeven if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
37689bde7b8SGeert Uytterhoeven "DMA sound", Q40Interrupt))
37789bde7b8SGeert Uytterhoeven return 0;
3781da177e4SLinus Torvalds
3791da177e4SLinus Torvalds return(1);
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds
3821da177e4SLinus Torvalds
3831da177e4SLinus Torvalds #ifdef MODULE
Q40IrqCleanUp(void)3841da177e4SLinus Torvalds static void Q40IrqCleanUp(void)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds master_outb(0,SAMPLE_ENABLE_REG);
3871da177e4SLinus Torvalds free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds #endif /* MODULE */
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds
Q40Silence(void)3921da177e4SLinus Torvalds static void Q40Silence(void)
3931da177e4SLinus Torvalds {
3941da177e4SLinus Torvalds master_outb(0,SAMPLE_ENABLE_REG);
3951da177e4SLinus Torvalds *DAC_LEFT=*DAC_RIGHT=127;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds
3981da177e4SLinus Torvalds static char *q40_pp;
3991da177e4SLinus Torvalds static unsigned int q40_sc;
4001da177e4SLinus Torvalds
Q40PlayNextFrame(int index)4011da177e4SLinus Torvalds static void Q40PlayNextFrame(int index)
4021da177e4SLinus Torvalds {
4031da177e4SLinus Torvalds u_char *start;
4041da177e4SLinus Torvalds u_long size;
4051da177e4SLinus Torvalds u_char speed;
40689bde7b8SGeert Uytterhoeven int error;
4071da177e4SLinus Torvalds
4081da177e4SLinus Torvalds /* used by Q40Play() if all doubts whether there really is something
4091da177e4SLinus Torvalds * to be played are already wiped out.
4101da177e4SLinus Torvalds */
4111da177e4SLinus Torvalds start = write_sq.buffers[write_sq.front];
4121da177e4SLinus Torvalds size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
4131da177e4SLinus Torvalds
4141da177e4SLinus Torvalds q40_pp=start;
4151da177e4SLinus Torvalds q40_sc=size;
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds write_sq.front = (write_sq.front+1) % write_sq.max_count;
4181da177e4SLinus Torvalds write_sq.active++;
4191da177e4SLinus Torvalds
4201da177e4SLinus Torvalds speed=(dmasound.hard.speed==10000 ? 0 : 1);
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds master_outb( 0,SAMPLE_ENABLE_REG);
4231da177e4SLinus Torvalds free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
4241da177e4SLinus Torvalds if (dmasound.soft.stereo)
42589bde7b8SGeert Uytterhoeven error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
4261da177e4SLinus Torvalds "Q40 sound", Q40Interrupt);
4271da177e4SLinus Torvalds else
42889bde7b8SGeert Uytterhoeven error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
4291da177e4SLinus Torvalds "Q40 sound", Q40Interrupt);
43089bde7b8SGeert Uytterhoeven if (error && printk_ratelimit())
43189bde7b8SGeert Uytterhoeven pr_err("Couldn't register sound interrupt\n");
4321da177e4SLinus Torvalds
4331da177e4SLinus Torvalds master_outb( speed, SAMPLE_RATE_REG);
4341da177e4SLinus Torvalds master_outb( 1,SAMPLE_CLEAR_REG);
4351da177e4SLinus Torvalds master_outb( 1,SAMPLE_ENABLE_REG);
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds
Q40Play(void)4381da177e4SLinus Torvalds static void Q40Play(void)
4391da177e4SLinus Torvalds {
4401da177e4SLinus Torvalds unsigned long flags;
4411da177e4SLinus Torvalds
4421da177e4SLinus Torvalds if (write_sq.active || write_sq.count<=0 ) {
4431da177e4SLinus Torvalds /* There's already a frame loaded */
4441da177e4SLinus Torvalds return;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds
4471da177e4SLinus Torvalds /* nothing in the queue */
4481da177e4SLinus Torvalds if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
4491da177e4SLinus Torvalds /* hmmm, the only existing frame is not
4501da177e4SLinus Torvalds * yet filled and we're not syncing?
4511da177e4SLinus Torvalds */
4521da177e4SLinus Torvalds return;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds spin_lock_irqsave(&dmasound.lock, flags);
4551da177e4SLinus Torvalds Q40PlayNextFrame(1);
4561da177e4SLinus Torvalds spin_unlock_irqrestore(&dmasound.lock, flags);
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds
Q40StereoInterrupt(int irq,void * dummy)4597d12e780SDavid Howells static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
4601da177e4SLinus Torvalds {
4611da177e4SLinus Torvalds spin_lock(&dmasound.lock);
4621da177e4SLinus Torvalds if (q40_sc>1){
4631da177e4SLinus Torvalds *DAC_LEFT=*q40_pp++;
4641da177e4SLinus Torvalds *DAC_RIGHT=*q40_pp++;
4651da177e4SLinus Torvalds q40_sc -=2;
4661da177e4SLinus Torvalds master_outb(1,SAMPLE_CLEAR_REG);
4671da177e4SLinus Torvalds }else Q40Interrupt();
4681da177e4SLinus Torvalds spin_unlock(&dmasound.lock);
4691da177e4SLinus Torvalds return IRQ_HANDLED;
4701da177e4SLinus Torvalds }
Q40MonoInterrupt(int irq,void * dummy)4717d12e780SDavid Howells static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
4721da177e4SLinus Torvalds {
4731da177e4SLinus Torvalds spin_lock(&dmasound.lock);
4741da177e4SLinus Torvalds if (q40_sc>0){
4751da177e4SLinus Torvalds *DAC_LEFT=*q40_pp;
4761da177e4SLinus Torvalds *DAC_RIGHT=*q40_pp++;
4771da177e4SLinus Torvalds q40_sc --;
4781da177e4SLinus Torvalds master_outb(1,SAMPLE_CLEAR_REG);
4791da177e4SLinus Torvalds }else Q40Interrupt();
4801da177e4SLinus Torvalds spin_unlock(&dmasound.lock);
4811da177e4SLinus Torvalds return IRQ_HANDLED;
4821da177e4SLinus Torvalds }
Q40Interrupt(void)4831da177e4SLinus Torvalds static void Q40Interrupt(void)
4841da177e4SLinus Torvalds {
4851da177e4SLinus Torvalds if (!write_sq.active) {
4861da177e4SLinus Torvalds /* playing was interrupted and sq_reset() has already cleared
4871da177e4SLinus Torvalds * the sq variables, so better don't do anything here.
4881da177e4SLinus Torvalds */
4891da177e4SLinus Torvalds WAKE_UP(write_sq.sync_queue);
4901da177e4SLinus Torvalds master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
4911da177e4SLinus Torvalds goto exit;
4921da177e4SLinus Torvalds } else write_sq.active=0;
4931da177e4SLinus Torvalds write_sq.count--;
4941da177e4SLinus Torvalds Q40Play();
4951da177e4SLinus Torvalds
4961da177e4SLinus Torvalds if (q40_sc<2)
4971da177e4SLinus Torvalds { /* there was nothing to play, disable irq */
4981da177e4SLinus Torvalds master_outb(0,SAMPLE_ENABLE_REG);
4991da177e4SLinus Torvalds *DAC_LEFT=*DAC_RIGHT=127;
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds WAKE_UP(write_sq.action_queue);
5021da177e4SLinus Torvalds
5031da177e4SLinus Torvalds exit:
5041da177e4SLinus Torvalds master_outb(1,SAMPLE_CLEAR_REG);
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds
5071da177e4SLinus Torvalds
Q40Init(void)5081da177e4SLinus Torvalds static void Q40Init(void)
5091da177e4SLinus Torvalds {
5101da177e4SLinus Torvalds int i, idx;
5111da177e4SLinus Torvalds const int freq[] = {10000, 20000};
5121da177e4SLinus Torvalds
5131da177e4SLinus Torvalds /* search a frequency that fits into the allowed error range */
5141da177e4SLinus Torvalds
5151da177e4SLinus Torvalds idx = -1;
5161da177e4SLinus Torvalds for (i = 0; i < 2; i++)
5171da177e4SLinus Torvalds if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
5181da177e4SLinus Torvalds idx = i;
5191da177e4SLinus Torvalds
5201da177e4SLinus Torvalds dmasound.hard = dmasound.soft;
5211da177e4SLinus Torvalds /*sound.hard.stereo=1;*/ /* no longer true */
5221da177e4SLinus Torvalds dmasound.hard.size=8;
5231da177e4SLinus Torvalds
5241da177e4SLinus Torvalds if (idx > -1) {
5251da177e4SLinus Torvalds dmasound.soft.speed = freq[idx];
5261da177e4SLinus Torvalds dmasound.trans_write = &transQ40Normal;
5271da177e4SLinus Torvalds } else
5281da177e4SLinus Torvalds dmasound.trans_write = &transQ40Expanding;
5291da177e4SLinus Torvalds
5301da177e4SLinus Torvalds Q40Silence();
5311da177e4SLinus Torvalds
5321da177e4SLinus Torvalds if (dmasound.hard.speed > 20200) {
5331da177e4SLinus Torvalds /* squeeze the sound, we do that */
5341da177e4SLinus Torvalds dmasound.hard.speed = 20000;
5351da177e4SLinus Torvalds dmasound.trans_write = &transQ40Compressing;
5361da177e4SLinus Torvalds } else if (dmasound.hard.speed > 10000) {
5371da177e4SLinus Torvalds dmasound.hard.speed = 20000;
5381da177e4SLinus Torvalds } else {
5391da177e4SLinus Torvalds dmasound.hard.speed = 10000;
5401da177e4SLinus Torvalds }
5411da177e4SLinus Torvalds expand_bal = -dmasound.soft.speed;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds
5441da177e4SLinus Torvalds
Q40SetFormat(int format)5451da177e4SLinus Torvalds static int Q40SetFormat(int format)
5461da177e4SLinus Torvalds {
5471da177e4SLinus Torvalds /* Q40 sound supports only 8bit modes */
5481da177e4SLinus Torvalds
5491da177e4SLinus Torvalds switch (format) {
5501da177e4SLinus Torvalds case AFMT_QUERY:
5511da177e4SLinus Torvalds return(dmasound.soft.format);
5521da177e4SLinus Torvalds case AFMT_MU_LAW:
5531da177e4SLinus Torvalds case AFMT_A_LAW:
5541da177e4SLinus Torvalds case AFMT_S8:
5551da177e4SLinus Torvalds case AFMT_U8:
5561da177e4SLinus Torvalds break;
5571da177e4SLinus Torvalds default:
5581da177e4SLinus Torvalds format = AFMT_S8;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds dmasound.soft.format = format;
5621da177e4SLinus Torvalds dmasound.soft.size = 8;
5631da177e4SLinus Torvalds if (dmasound.minDev == SND_DEV_DSP) {
5641da177e4SLinus Torvalds dmasound.dsp.format = format;
5651da177e4SLinus Torvalds dmasound.dsp.size = 8;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds Q40Init();
5681da177e4SLinus Torvalds
5691da177e4SLinus Torvalds return(format);
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds
Q40SetVolume(int volume)5721da177e4SLinus Torvalds static int Q40SetVolume(int volume)
5731da177e4SLinus Torvalds {
5741da177e4SLinus Torvalds return 0;
5751da177e4SLinus Torvalds }
5761da177e4SLinus Torvalds
5771da177e4SLinus Torvalds
5781da177e4SLinus Torvalds /*** Machine definitions *****************************************************/
5791da177e4SLinus Torvalds
5801da177e4SLinus Torvalds static SETTINGS def_hard = {
5811da177e4SLinus Torvalds .format = AFMT_U8,
5821da177e4SLinus Torvalds .stereo = 0,
5831da177e4SLinus Torvalds .size = 8,
5841da177e4SLinus Torvalds .speed = 10000
5851da177e4SLinus Torvalds } ;
5861da177e4SLinus Torvalds
5871da177e4SLinus Torvalds static SETTINGS def_soft = {
5881da177e4SLinus Torvalds .format = AFMT_U8,
5891da177e4SLinus Torvalds .stereo = 0,
5901da177e4SLinus Torvalds .size = 8,
5911da177e4SLinus Torvalds .speed = 8000
5921da177e4SLinus Torvalds } ;
5931da177e4SLinus Torvalds
5941da177e4SLinus Torvalds static MACHINE machQ40 = {
5951da177e4SLinus Torvalds .name = "Q40",
5961da177e4SLinus Torvalds .name2 = "Q40",
5971da177e4SLinus Torvalds .owner = THIS_MODULE,
5981da177e4SLinus Torvalds .dma_alloc = Q40Alloc,
5991da177e4SLinus Torvalds .dma_free = Q40Free,
6001da177e4SLinus Torvalds .irqinit = Q40IrqInit,
6011da177e4SLinus Torvalds #ifdef MODULE
6021da177e4SLinus Torvalds .irqcleanup = Q40IrqCleanUp,
6031da177e4SLinus Torvalds #endif /* MODULE */
6041da177e4SLinus Torvalds .init = Q40Init,
6051da177e4SLinus Torvalds .silence = Q40Silence,
6061da177e4SLinus Torvalds .setFormat = Q40SetFormat,
6071da177e4SLinus Torvalds .setVolume = Q40SetVolume,
6081da177e4SLinus Torvalds .play = Q40Play,
6091da177e4SLinus Torvalds .min_dsp_speed = 10000,
6101da177e4SLinus Torvalds .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
6111da177e4SLinus Torvalds .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
6121da177e4SLinus Torvalds .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
6131da177e4SLinus Torvalds };
6141da177e4SLinus Torvalds
6151da177e4SLinus Torvalds
6161da177e4SLinus Torvalds /*** Config & Setup **********************************************************/
6171da177e4SLinus Torvalds
6181da177e4SLinus Torvalds
dmasound_q40_init(void)6190a1b42dbSAdrian Bunk static int __init dmasound_q40_init(void)
6201da177e4SLinus Torvalds {
6211da177e4SLinus Torvalds if (MACH_IS_Q40) {
6221da177e4SLinus Torvalds dmasound.mach = machQ40;
6231da177e4SLinus Torvalds dmasound.mach.default_hard = def_hard ;
6241da177e4SLinus Torvalds dmasound.mach.default_soft = def_soft ;
6251da177e4SLinus Torvalds return dmasound_init();
6261da177e4SLinus Torvalds } else
6271da177e4SLinus Torvalds return -ENODEV;
6281da177e4SLinus Torvalds }
6291da177e4SLinus Torvalds
dmasound_q40_cleanup(void)6301da177e4SLinus Torvalds static void __exit dmasound_q40_cleanup(void)
6311da177e4SLinus Torvalds {
6321da177e4SLinus Torvalds dmasound_deinit();
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds
6351da177e4SLinus Torvalds module_init(dmasound_q40_init);
6361da177e4SLinus Torvalds module_exit(dmasound_q40_cleanup);
6371da177e4SLinus Torvalds
6381da177e4SLinus Torvalds MODULE_DESCRIPTION("Q40/Q60 sound driver");
6391da177e4SLinus Torvalds MODULE_LICENSE("GPL");
640