brintos

brintos / linux-shallow public Read only

0
0
Text · 14.1 KiB · e25a78d Raw
640 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/sound/oss/dmasound/dmasound_q40.c4 *5 *  Q40 DMA Sound Driver6 *7 *  See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits8 *  prior to 28/01/20019 *10 *  28/01/2001 [0.1] Iain Sandoe11 *		     - added versioning12 *		     - put in and populated the hardware_afmts field.13 *             [0.2] - put in SNDCTL_DSP_GETCAPS value.14 *	       [0.3] - put in default hard/soft settings.15 */16 17 18#include <linux/module.h>19#include <linux/init.h>20#include <linux/slab.h>21#include <linux/soundcard.h>22#include <linux/interrupt.h>23 24#include <linux/uaccess.h>25#include <asm/q40ints.h>26#include <asm/q40_master.h>27 28#include "dmasound.h"29 30#define DMASOUND_Q40_REVISION 031#define DMASOUND_Q40_EDITION 332 33static int expand_bal;	/* Balance factor for expanding (not volume!) */34static int expand_data;	/* Data for expanding */35 36 37/*** Low level stuff *********************************************************/38 39 40static void *Q40Alloc(unsigned int size, gfp_t flags);41static void Q40Free(void *, unsigned int);42static int Q40IrqInit(void);43#ifdef MODULE44static void Q40IrqCleanUp(void);45#endif46static void Q40Silence(void);47static void Q40Init(void);48static int Q40SetFormat(int format);49static int Q40SetVolume(int volume);50static void Q40PlayNextFrame(int index);51static void Q40Play(void);52static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);53static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);54static void Q40Interrupt(void);55 56 57/*** Mid level stuff *********************************************************/58 59 60 61/* userCount, frameUsed, frameLeft == byte counts */62static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,63			   u_char frame[], ssize_t *frameUsed,64			   ssize_t frameLeft)65{66	char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;67	ssize_t count, used;68	u_char *p = (u_char *) &frame[*frameUsed];69 70	used = count = min_t(size_t, userCount, frameLeft);71	if (copy_from_user(p,userPtr,count))72	  return -EFAULT;73	while (count > 0) {74		*p = table[*p]+128;75		p++;76		count--;77	}78	*frameUsed += used ;79	return used;80}81 82 83static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,84			  u_char frame[], ssize_t *frameUsed,85			  ssize_t frameLeft)86{87	ssize_t count, used;88	u_char *p = (u_char *) &frame[*frameUsed];89 90	used = count = min_t(size_t, userCount, frameLeft);91	if (copy_from_user(p,userPtr,count))92	  return -EFAULT;93	while (count > 0) {94		*p = *p + 128;95		p++;96		count--;97	}98	*frameUsed += used;99	return used;100}101 102static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,103			  u_char frame[], ssize_t *frameUsed,104			  ssize_t frameLeft)105{106	ssize_t count, used;107	u_char *p = (u_char *) &frame[*frameUsed];108 109	used = count = min_t(size_t, userCount, frameLeft);110	if (copy_from_user(p,userPtr,count))111	  return -EFAULT;112	*frameUsed += used;113	return used;114}115 116 117/* a bit too complicated to optimise right now ..*/118static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,119			    u_char frame[], ssize_t *frameUsed,120			    ssize_t frameLeft)121{122	unsigned char *table = (unsigned char *)123		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);124	unsigned int data = expand_data;125	u_char *p = (u_char *) &frame[*frameUsed];126	int bal = expand_bal;127	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;128	int utotal, ftotal;129 130	ftotal = frameLeft;131	utotal = userCount;132	while (frameLeft) {133		u_char c;134		if (bal < 0) {135			if (userCount == 0)136				break;137			if (get_user(c, userPtr++))138				return -EFAULT;139			data = table[c];140			data += 0x80;141			userCount--;142			bal += hSpeed;143		}144		*p++ = data;145		frameLeft--;146		bal -= sSpeed;147	}148	expand_bal = bal;149	expand_data = data;150	*frameUsed += (ftotal - frameLeft);151	utotal -= userCount;152	return utotal;153}154 155 156static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,157			   u_char frame[], ssize_t *frameUsed,158			   ssize_t frameLeft)159{160	u_char *p = (u_char *) &frame[*frameUsed];161	unsigned int data = expand_data;162	int bal = expand_bal;163	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;164	int utotal, ftotal;165 166 167	ftotal = frameLeft;168	utotal = userCount;169	while (frameLeft) {170		u_char c;171		if (bal < 0) {172			if (userCount == 0)173				break;174			if (get_user(c, userPtr++))175				return -EFAULT;176			data = c ;177			data += 0x80;178			userCount--;179			bal += hSpeed;180		}181		*p++ = data;182		frameLeft--;183		bal -= sSpeed;184	}185	expand_bal = bal;186	expand_data = data;187	*frameUsed += (ftotal - frameLeft);188	utotal -= userCount;189	return utotal;190}191 192 193static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,194			   u_char frame[], ssize_t *frameUsed,195			   ssize_t frameLeft)196{197	u_char *p = (u_char *) &frame[*frameUsed];198	unsigned int data = expand_data;199	int bal = expand_bal;200	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;201	int utotal, ftotal;202 203	ftotal = frameLeft;204	utotal = userCount;205	while (frameLeft) {206		u_char c;207		if (bal < 0) {208			if (userCount == 0)209				break;210			if (get_user(c, userPtr++))211				return -EFAULT;212			data = c ;213			userCount--;214			bal += hSpeed;215		}216		*p++ = data;217		frameLeft--;218		bal -= sSpeed;219	}220	expand_bal = bal;221	expand_data = data;222	*frameUsed += (ftotal - frameLeft) ;223	utotal -= userCount;224	return utotal;225}226 227/* compressing versions */228static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,229			    u_char frame[], ssize_t *frameUsed,230			    ssize_t frameLeft)231{232	unsigned char *table = (unsigned char *)233		(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);234	unsigned int data = expand_data;235	u_char *p = (u_char *) &frame[*frameUsed];236	int bal = expand_bal;237	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;238	int utotal, ftotal;239 240	ftotal = frameLeft;241	utotal = userCount;242	while (frameLeft) {243		u_char c;244		while(bal<0) {245			if (userCount == 0)246				goto lout;247			if (!(bal<(-hSpeed))) {248				if (get_user(c, userPtr))249					return -EFAULT;250				data = 0x80 + table[c];251			}252			userPtr++;253			userCount--;254			bal += hSpeed;255		}256		*p++ = data;257		frameLeft--;258		bal -= sSpeed;259	}260 lout:261	expand_bal = bal;262	expand_data = data;263	*frameUsed += (ftotal - frameLeft);264	utotal -= userCount;265	return utotal;266}267 268 269static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,270			   u_char frame[], ssize_t *frameUsed,271			   ssize_t frameLeft)272{273	u_char *p = (u_char *) &frame[*frameUsed];274	unsigned int data = expand_data;275	int bal = expand_bal;276	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;277	int utotal, ftotal;278 279	ftotal = frameLeft;280	utotal = userCount;281	while (frameLeft) {282		u_char c;283		while (bal < 0) {284			if (userCount == 0)285				goto lout;286			if (!(bal<(-hSpeed))) {287				if (get_user(c, userPtr))288					return -EFAULT;289				data = c + 0x80;290			}291			userPtr++;292			userCount--;293			bal += hSpeed;294		}295		*p++ = data;296		frameLeft--;297		bal -= sSpeed;298	}299 lout:300	expand_bal = bal;301	expand_data = data;302	*frameUsed += (ftotal - frameLeft);303	utotal -= userCount;304	return utotal;305}306 307 308static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,309			   u_char frame[], ssize_t *frameUsed,310			   ssize_t frameLeft)311{312	u_char *p = (u_char *) &frame[*frameUsed];313	unsigned int data = expand_data;314	int bal = expand_bal;315	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;316	int utotal, ftotal;317 318	ftotal = frameLeft;319	utotal = userCount;320	while (frameLeft) {321		u_char c;322		while (bal < 0) {323			if (userCount == 0)324				goto lout;325			if (!(bal<(-hSpeed))) {326				if (get_user(c, userPtr))327					return -EFAULT;328				data = c ;329			}330			userPtr++;331			userCount--;332			bal += hSpeed;333		}334		*p++ = data;335		frameLeft--;336		bal -= sSpeed;337	}338 lout:339	expand_bal = bal;340	expand_data = data;341	*frameUsed += (ftotal - frameLeft) ;342	utotal -= userCount;343	return utotal;344}345 346 347static TRANS transQ40Normal = {348	q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL349};350 351static TRANS transQ40Expanding = {352	q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL353};354 355static TRANS transQ40Compressing = {356	q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL357};358 359 360/*** Low level stuff *********************************************************/361 362static void *Q40Alloc(unsigned int size, gfp_t flags)363{364         return kmalloc(size, flags); /* change to vmalloc */365}366 367static void Q40Free(void *ptr, unsigned int size)368{369	kfree(ptr);370}371 372static int __init Q40IrqInit(void)373{374	/* Register interrupt handler. */375	if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,376		    "DMA sound", Q40Interrupt))377		return 0;378 379	return(1);380}381 382 383#ifdef MODULE384static void Q40IrqCleanUp(void)385{386        master_outb(0,SAMPLE_ENABLE_REG);387	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);388}389#endif /* MODULE */390 391 392static void Q40Silence(void)393{394        master_outb(0,SAMPLE_ENABLE_REG);395	*DAC_LEFT=*DAC_RIGHT=127;396}397 398static char *q40_pp;399static unsigned int q40_sc;400 401static void Q40PlayNextFrame(int index)402{403	u_char *start;404	u_long size;405	u_char speed;406	int error;407 408	/* used by Q40Play() if all doubts whether there really is something409	 * to be played are already wiped out.410	 */411	start = write_sq.buffers[write_sq.front];412	size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);413 414	q40_pp=start;415	q40_sc=size;416 417	write_sq.front = (write_sq.front+1) % write_sq.max_count;418	write_sq.active++;419 420	speed=(dmasound.hard.speed==10000 ? 0 : 1);421 422	master_outb( 0,SAMPLE_ENABLE_REG);423	free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);424	if (dmasound.soft.stereo)425		error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,426				    "Q40 sound", Q40Interrupt);427	  else428		error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,429				    "Q40 sound", Q40Interrupt);430	if (error && printk_ratelimit())431		pr_err("Couldn't register sound interrupt\n");432 433	master_outb( speed, SAMPLE_RATE_REG);434	master_outb( 1,SAMPLE_CLEAR_REG);435	master_outb( 1,SAMPLE_ENABLE_REG);436}437 438static void Q40Play(void)439{440        unsigned long flags;441 442	if (write_sq.active || write_sq.count<=0 ) {443		/* There's already a frame loaded */444		return;445	}446 447	/* nothing in the queue */448	if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {449	         /* hmmm, the only existing frame is not450		  * yet filled and we're not syncing?451		  */452	         return;453	}454	spin_lock_irqsave(&dmasound.lock, flags);455	Q40PlayNextFrame(1);456	spin_unlock_irqrestore(&dmasound.lock, flags);457}458 459static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)460{461	spin_lock(&dmasound.lock);462        if (q40_sc>1){463            *DAC_LEFT=*q40_pp++;464	    *DAC_RIGHT=*q40_pp++;465	    q40_sc -=2;466	    master_outb(1,SAMPLE_CLEAR_REG);467	}else Q40Interrupt();468	spin_unlock(&dmasound.lock);469	return IRQ_HANDLED;470}471static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)472{473	spin_lock(&dmasound.lock);474        if (q40_sc>0){475            *DAC_LEFT=*q40_pp;476	    *DAC_RIGHT=*q40_pp++;477	    q40_sc --;478	    master_outb(1,SAMPLE_CLEAR_REG);479	}else Q40Interrupt();480	spin_unlock(&dmasound.lock);481	return IRQ_HANDLED;482}483static void Q40Interrupt(void)484{485	if (!write_sq.active) {486	          /* playing was interrupted and sq_reset() has already cleared487		   * the sq variables, so better don't do anything here.488		   */489	           WAKE_UP(write_sq.sync_queue);490		   master_outb(0,SAMPLE_ENABLE_REG); /* better safe */491		   goto exit;492	} else write_sq.active=0;493	write_sq.count--;494	Q40Play();495 496	if (q40_sc<2)497	      { /* there was nothing to play, disable irq */498		master_outb(0,SAMPLE_ENABLE_REG);499		*DAC_LEFT=*DAC_RIGHT=127;500	      }501	WAKE_UP(write_sq.action_queue);502 503 exit:504	master_outb(1,SAMPLE_CLEAR_REG);505}506 507 508static void Q40Init(void)509{510	int i, idx;511	const int freq[] = {10000, 20000};512 513	/* search a frequency that fits into the allowed error range */514 515	idx = -1;516	for (i = 0; i < 2; i++)517		if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)518			idx = i;519 520	dmasound.hard = dmasound.soft;521	/*sound.hard.stereo=1;*/ /* no longer true */522	dmasound.hard.size=8;523 524	if (idx > -1) {525		dmasound.soft.speed = freq[idx];526		dmasound.trans_write = &transQ40Normal;527	} else528		dmasound.trans_write = &transQ40Expanding;529 530	Q40Silence();531 532	if (dmasound.hard.speed > 20200) {533		/* squeeze the sound, we do that */534		dmasound.hard.speed = 20000;535		dmasound.trans_write = &transQ40Compressing;536	} else if (dmasound.hard.speed > 10000) {537		dmasound.hard.speed = 20000;538	} else {539		dmasound.hard.speed = 10000;540	}541	expand_bal = -dmasound.soft.speed;542}543 544 545static int Q40SetFormat(int format)546{547	/* Q40 sound supports only 8bit modes */548 549	switch (format) {550	case AFMT_QUERY:551		return(dmasound.soft.format);552	case AFMT_MU_LAW:553	case AFMT_A_LAW:554	case AFMT_S8:555	case AFMT_U8:556		break;557	default:558		format = AFMT_S8;559	}560 561	dmasound.soft.format = format;562	dmasound.soft.size = 8;563	if (dmasound.minDev == SND_DEV_DSP) {564		dmasound.dsp.format = format;565		dmasound.dsp.size = 8;566	}567	Q40Init();568 569	return(format);570}571 572static int Q40SetVolume(int volume)573{574    return 0;575}576 577 578/*** Machine definitions *****************************************************/579 580static SETTINGS def_hard = {581	.format	= AFMT_U8,582	.stereo	= 0,583	.size	= 8,584	.speed	= 10000585} ;586 587static SETTINGS def_soft = {588	.format	= AFMT_U8,589	.stereo	= 0,590	.size	= 8,591	.speed	= 8000592} ;593 594static MACHINE machQ40 = {595	.name		= "Q40",596	.name2		= "Q40",597	.owner		= THIS_MODULE,598	.dma_alloc	= Q40Alloc,599	.dma_free	= Q40Free,600	.irqinit	= Q40IrqInit,601#ifdef MODULE602	.irqcleanup	= Q40IrqCleanUp,603#endif /* MODULE */604	.init		= Q40Init,605	.silence	= Q40Silence,606	.setFormat	= Q40SetFormat,607	.setVolume	= Q40SetVolume,608	.play		= Q40Play,609 	.min_dsp_speed	= 10000,610	.version	= ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),611	.hardware_afmts	= AFMT_U8, /* h'ware-supported formats *only* here */612	.capabilities	= DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */613};614 615 616/*** Config & Setup **********************************************************/617 618 619static int __init dmasound_q40_init(void)620{621	if (MACH_IS_Q40) {622	    dmasound.mach = machQ40;623	    dmasound.mach.default_hard = def_hard ;624	    dmasound.mach.default_soft = def_soft ;625	    return dmasound_init();626	} else627	    return -ENODEV;628}629 630static void __exit dmasound_q40_cleanup(void)631{632	dmasound_deinit();633}634 635module_init(dmasound_q40_init);636module_exit(dmasound_q40_cleanup);637 638MODULE_DESCRIPTION("Q40/Q60 sound driver");639MODULE_LICENSE("GPL");640