109c434b8SThomas 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