brintos

brintos / linux-shallow public Read only

0
0
Text · 19.0 KiB · b8fad12 Raw
746 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  linux/sound/oss/dmasound/dmasound_paula.c4 *5 *  Amiga `Paula' 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 constraint on state buffer usage.15 *	       [0.4] - put in default hard/soft settings16*/17 18 19#include <linux/module.h>20#include <linux/mm.h>21#include <linux/init.h>22#include <linux/ioport.h>23#include <linux/soundcard.h>24#include <linux/interrupt.h>25#include <linux/platform_device.h>26 27#include <linux/uaccess.h>28#include <asm/setup.h>29#include <asm/amigahw.h>30#include <asm/amigaints.h>31#include <asm/machdep.h>32 33#include "dmasound.h"34 35#define DMASOUND_PAULA_REVISION 036#define DMASOUND_PAULA_EDITION 437 38#define custom amiga_custom39   /*40    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)41    *	(Imported from arch/m68k/amiga/amisound.c)42    */43 44extern volatile u_short amiga_audio_min_period;45 46 47   /*48    *	amiga_mksound() should be able to restore the period after beeping49    *	(Imported from arch/m68k/amiga/amisound.c)50    */51 52extern u_short amiga_audio_period;53 54 55   /*56    *	Audio DMA masks57    */58 59#define AMI_AUDIO_OFF	(DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)60#define AMI_AUDIO_8	(DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)61#define AMI_AUDIO_14	(AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)62 63 64    /*65     *  Helper pointers for 16(14)-bit sound66     */67 68static int write_sq_block_size_half, write_sq_block_size_quarter;69 70 71/*** Low level stuff *********************************************************/72 73 74static void *AmiAlloc(unsigned int size, gfp_t flags);75static void AmiFree(void *obj, unsigned int size);76static int AmiIrqInit(void);77#ifdef MODULE78static void AmiIrqCleanUp(void);79#endif80static void AmiSilence(void);81static void AmiInit(void);82static int AmiSetFormat(int format);83static int AmiSetVolume(int volume);84static int AmiSetTreble(int treble);85static void AmiPlayNextFrame(int index);86static void AmiPlay(void);87static irqreturn_t AmiInterrupt(int irq, void *dummy);88 89#ifdef CONFIG_HEARTBEAT90 91    /*92     *  Heartbeat interferes with sound since the 7 kHz low-pass filter and the93     *  power LED are controlled by the same line.94     */95 96static void (*saved_heartbeat)(int) = NULL;97 98static inline void disable_heartbeat(void)99{100	if (mach_heartbeat) {101	    saved_heartbeat = mach_heartbeat;102	    mach_heartbeat = NULL;103	}104	AmiSetTreble(dmasound.treble);105}106 107static inline void enable_heartbeat(void)108{109	if (saved_heartbeat)110	    mach_heartbeat = saved_heartbeat;111}112#else /* !CONFIG_HEARTBEAT */113#define disable_heartbeat()	do { } while (0)114#define enable_heartbeat()	do { } while (0)115#endif /* !CONFIG_HEARTBEAT */116 117 118/*** Mid level stuff *********************************************************/119 120static void AmiMixerInit(void);121static int AmiMixerIoctl(u_int cmd, u_long arg);122static int AmiWriteSqSetup(void);123static int AmiStateInfo(char *buffer, size_t space);124 125 126/*** Translations ************************************************************/127 128/* ++TeSche: radically changed for new expanding purposes...129 *130 * These two routines now deal with copying/expanding/translating the samples131 * from user space into our buffer at the right frequency. They take care about132 * how much data there's actually to read, how much buffer space there is and133 * to convert samples into the right frequency/encoding. They will only work on134 * complete samples so it may happen they leave some bytes in the input stream135 * if the user didn't write a multiple of the current sample size. They both136 * return the number of bytes they've used from both streams so you may detect137 * such a situation. Luckily all programs should be able to cope with that.138 *139 * I think I've optimized anything as far as one can do in plain C, all140 * variables should fit in registers and the loops are really short. There's141 * one loop for every possible situation. Writing a more generalized and thus142 * parameterized loop would only produce slower code. Feel free to optimize143 * this in assembler if you like. :)144 *145 * I think these routines belong here because they're not yet really hardware146 * independent, especially the fact that the Falcon can play 16bit samples147 * only in stereo is hardcoded in both of them!148 *149 * ++geert: split in even more functions (one per format)150 */151 152 153    /*154     *  Native format155     */156 157static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,158			 u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)159{160	ssize_t count, used;161 162	if (!dmasound.soft.stereo) {163		void *p = &frame[*frameUsed];164		count = min_t(unsigned long, userCount, frameLeft) & ~1;165		used = count;166		if (copy_from_user(p, userPtr, count))167			return -EFAULT;168	} else {169		u_char *left = &frame[*frameUsed>>1];170		u_char *right = left+write_sq_block_size_half;171		count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;172		used = count*2;173		while (count > 0) {174			if (get_user(*left++, userPtr++)175			    || get_user(*right++, userPtr++))176				return -EFAULT;177			count--;178		}179	}180	*frameUsed += used;181	return used;182}183 184 185    /*186     *  Copy and convert 8 bit data187     */188 189#define GENERATE_AMI_CT8(funcname, convsample)				\190static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\191			u_char frame[], ssize_t *frameUsed,		\192			ssize_t frameLeft)				\193{									\194	ssize_t count, used;						\195									\196	if (!dmasound.soft.stereo) {					\197		u_char *p = &frame[*frameUsed];				\198		count = min_t(size_t, userCount, frameLeft) & ~1;	\199		used = count;						\200		while (count > 0) {					\201			u_char data;					\202			if (get_user(data, userPtr++))			\203				return -EFAULT;				\204			*p++ = convsample(data);			\205			count--;					\206		}							\207	} else {							\208		u_char *left = &frame[*frameUsed>>1];			\209		u_char *right = left+write_sq_block_size_half;		\210		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\211		used = count*2;						\212		while (count > 0) {					\213			u_char data;					\214			if (get_user(data, userPtr++))			\215				return -EFAULT;				\216			*left++ = convsample(data);			\217			if (get_user(data, userPtr++))			\218				return -EFAULT;				\219			*right++ = convsample(data);			\220			count--;					\221		}							\222	}								\223	*frameUsed += used;						\224	return used;							\225}226 227#define AMI_CT_ULAW(x)	(dmasound_ulaw2dma8[(x)])228#define AMI_CT_ALAW(x)	(dmasound_alaw2dma8[(x)])229#define AMI_CT_U8(x)	((x) ^ 0x80)230 231GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)232GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)233GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)234 235 236    /*237     *  Copy and convert 16 bit data238     */239 240#define GENERATE_AMI_CT_16(funcname, convsample)			\241static ssize_t funcname(const u_char __user *userPtr, size_t userCount,	\242			u_char frame[], ssize_t *frameUsed,		\243			ssize_t frameLeft)				\244{									\245	const u_short __user *ptr = (const u_short __user *)userPtr;	\246	ssize_t count, used;						\247	u_short data;							\248									\249	if (!dmasound.soft.stereo) {					\250		u_char *high = &frame[*frameUsed>>1];			\251		u_char *low = high+write_sq_block_size_half;		\252		count = min_t(size_t, userCount, frameLeft)>>1 & ~1;	\253		used = count*2;						\254		while (count > 0) {					\255			if (get_user(data, ptr++))			\256				return -EFAULT;				\257			data = convsample(data);			\258			*high++ = data>>8;				\259			*low++ = (data>>2) & 0x3f;			\260			count--;					\261		}							\262	} else {							\263		u_char *lefth = &frame[*frameUsed>>2];			\264		u_char *leftl = lefth+write_sq_block_size_quarter;	\265		u_char *righth = lefth+write_sq_block_size_half;	\266		u_char *rightl = righth+write_sq_block_size_quarter;	\267		count = min_t(size_t, userCount, frameLeft)>>2 & ~1;	\268		used = count*4;						\269		while (count > 0) {					\270			if (get_user(data, ptr++))			\271				return -EFAULT;				\272			data = convsample(data);			\273			*lefth++ = data>>8;				\274			*leftl++ = (data>>2) & 0x3f;			\275			if (get_user(data, ptr++))			\276				return -EFAULT;				\277			data = convsample(data);			\278			*righth++ = data>>8;				\279			*rightl++ = (data>>2) & 0x3f;			\280			count--;					\281		}							\282	}								\283	*frameUsed += used;						\284	return used;							\285}286 287#define AMI_CT_S16BE(x)	(x)288#define AMI_CT_U16BE(x)	((x) ^ 0x8000)289#define AMI_CT_S16LE(x)	(le2be16((x)))290#define AMI_CT_U16LE(x)	(le2be16((x)) ^ 0x8000)291 292GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)293GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)294GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)295GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)296 297 298static TRANS transAmiga = {299	.ct_ulaw	= ami_ct_ulaw,300	.ct_alaw	= ami_ct_alaw,301	.ct_s8		= ami_ct_s8,302	.ct_u8		= ami_ct_u8,303	.ct_s16be	= ami_ct_s16be,304	.ct_u16be	= ami_ct_u16be,305	.ct_s16le	= ami_ct_s16le,306	.ct_u16le	= ami_ct_u16le,307};308 309/*** Low level stuff *********************************************************/310 311static inline void StopDMA(void)312{313	custom.aud[0].audvol = custom.aud[1].audvol = 0;314	custom.aud[2].audvol = custom.aud[3].audvol = 0;315	custom.dmacon = AMI_AUDIO_OFF;316	enable_heartbeat();317}318 319static void *AmiAlloc(unsigned int size, gfp_t flags)320{321	return amiga_chip_alloc((long)size, "dmasound [Paula]");322}323 324static void AmiFree(void *obj, unsigned int size)325{326	amiga_chip_free (obj);327}328 329static int __init AmiIrqInit(void)330{331	/* turn off DMA for audio channels */332	StopDMA();333 334	/* Register interrupt handler. */335	if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",336			AmiInterrupt))337		return 0;338	return 1;339}340 341#ifdef MODULE342static void AmiIrqCleanUp(void)343{344	/* turn off DMA for audio channels */345	StopDMA();346	/* release the interrupt */347	free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);348}349#endif /* MODULE */350 351static void AmiSilence(void)352{353	/* turn off DMA for audio channels */354	StopDMA();355}356 357 358static void AmiInit(void)359{360	int period, i;361 362	AmiSilence();363 364	if (dmasound.soft.speed)365		period = amiga_colorclock/dmasound.soft.speed-1;366	else367		period = amiga_audio_min_period;368	dmasound.hard = dmasound.soft;369	dmasound.trans_write = &transAmiga;370 371	if (period < amiga_audio_min_period) {372		/* we would need to squeeze the sound, but we won't do that */373		period = amiga_audio_min_period;374	} else if (period > 65535) {375		period = 65535;376	}377	dmasound.hard.speed = amiga_colorclock/(period+1);378 379	for (i = 0; i < 4; i++)380		custom.aud[i].audper = period;381	amiga_audio_period = period;382}383 384 385static int AmiSetFormat(int format)386{387	int size;388 389	/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */390 391	switch (format) {392	case AFMT_QUERY:393		return dmasound.soft.format;394	case AFMT_MU_LAW:395	case AFMT_A_LAW:396	case AFMT_U8:397	case AFMT_S8:398		size = 8;399		break;400	case AFMT_S16_BE:401	case AFMT_U16_BE:402	case AFMT_S16_LE:403	case AFMT_U16_LE:404		size = 16;405		break;406	default: /* :-) */407		size = 8;408		format = AFMT_S8;409	}410 411	dmasound.soft.format = format;412	dmasound.soft.size = size;413	if (dmasound.minDev == SND_DEV_DSP) {414		dmasound.dsp.format = format;415		dmasound.dsp.size = dmasound.soft.size;416	}417	AmiInit();418 419	return format;420}421 422 423#define VOLUME_VOXWARE_TO_AMI(v) \424	(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)425#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)426 427static int AmiSetVolume(int volume)428{429	dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);430	custom.aud[0].audvol = dmasound.volume_left;431	dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);432	custom.aud[1].audvol = dmasound.volume_right;433	if (dmasound.hard.size == 16) {434		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {435			custom.aud[2].audvol = 1;436			custom.aud[3].audvol = 1;437		} else {438			custom.aud[2].audvol = 0;439			custom.aud[3].audvol = 0;440		}441	}442	return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |443	       (VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);444}445 446static int AmiSetTreble(int treble)447{448	dmasound.treble = treble;449	if (treble < 50)450		ciaa.pra &= ~0x02;451	else452		ciaa.pra |= 0x02;453	return treble;454}455 456 457#define AMI_PLAY_LOADED		1458#define AMI_PLAY_PLAYING	2459#define AMI_PLAY_MASK		3460 461 462static void AmiPlayNextFrame(int index)463{464	u_char *start, *ch0, *ch1, *ch2, *ch3;465	u_long size;466 467	/* used by AmiPlay() if all doubts whether there really is something468	 * to be played are already wiped out.469	 */470	start = write_sq.buffers[write_sq.front];471	size = (write_sq.count == index ? write_sq.rear_size472					: write_sq.block_size)>>1;473 474	if (dmasound.hard.stereo) {475		ch0 = start;476		ch1 = start+write_sq_block_size_half;477		size >>= 1;478	} else {479		ch0 = start;480		ch1 = start;481	}482 483	disable_heartbeat();484	custom.aud[0].audvol = dmasound.volume_left;485	custom.aud[1].audvol = dmasound.volume_right;486	if (dmasound.hard.size == 8) {487		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);488		custom.aud[0].audlen = size;489		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);490		custom.aud[1].audlen = size;491		custom.dmacon = AMI_AUDIO_8;492	} else {493		size >>= 1;494		custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);495		custom.aud[0].audlen = size;496		custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);497		custom.aud[1].audlen = size;498		if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {499			/* We can play pseudo 14-bit only with the maximum volume */500			ch3 = ch0+write_sq_block_size_quarter;501			ch2 = ch1+write_sq_block_size_quarter;502			custom.aud[2].audvol = 1;  /* we are being affected by the beeps */503			custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */504			custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);505			custom.aud[2].audlen = size;506			custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);507			custom.aud[3].audlen = size;508			custom.dmacon = AMI_AUDIO_14;509		} else {510			custom.aud[2].audvol = 0;511			custom.aud[3].audvol = 0;512			custom.dmacon = AMI_AUDIO_8;513		}514	}515	write_sq.front = (write_sq.front+1) % write_sq.max_count;516	write_sq.active |= AMI_PLAY_LOADED;517}518 519 520static void AmiPlay(void)521{522	int minframes = 1;523 524	custom.intena = IF_AUD0;525 526	if (write_sq.active & AMI_PLAY_LOADED) {527		/* There's already a frame loaded */528		custom.intena = IF_SETCLR | IF_AUD0;529		return;530	}531 532	if (write_sq.active & AMI_PLAY_PLAYING)533		/* Increase threshold: frame 1 is already being played */534		minframes = 2;535 536	if (write_sq.count < minframes) {537		/* Nothing to do */538		custom.intena = IF_SETCLR | IF_AUD0;539		return;540	}541 542	if (write_sq.count <= minframes &&543	    write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {544		/* hmmm, the only existing frame is not545		 * yet filled and we're not syncing?546		 */547		custom.intena = IF_SETCLR | IF_AUD0;548		return;549	}550 551	AmiPlayNextFrame(minframes);552 553	custom.intena = IF_SETCLR | IF_AUD0;554}555 556 557static irqreturn_t AmiInterrupt(int irq, void *dummy)558{559	int minframes = 1;560 561	custom.intena = IF_AUD0;562 563	if (!write_sq.active) {564		/* Playing was interrupted and sq_reset() has already cleared565		 * the sq variables, so better don't do anything here.566		 */567		WAKE_UP(write_sq.sync_queue);568		return IRQ_HANDLED;569	}570 571	if (write_sq.active & AMI_PLAY_PLAYING) {572		/* We've just finished a frame */573		write_sq.count--;574		WAKE_UP(write_sq.action_queue);575	}576 577	if (write_sq.active & AMI_PLAY_LOADED)578		/* Increase threshold: frame 1 is already being played */579		minframes = 2;580 581	/* Shift the flags */582	write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;583 584	if (!write_sq.active)585		/* No frame is playing, disable audio DMA */586		StopDMA();587 588	custom.intena = IF_SETCLR | IF_AUD0;589 590	if (write_sq.count >= minframes)591		/* Try to play the next frame */592		AmiPlay();593 594	if (!write_sq.active)595		/* Nothing to play anymore.596		   Wake up a process waiting for audio output to drain. */597		WAKE_UP(write_sq.sync_queue);598	return IRQ_HANDLED;599}600 601/*** Mid level stuff *********************************************************/602 603 604/*605 * /dev/mixer abstraction606 */607 608static void __init AmiMixerInit(void)609{610	dmasound.volume_left = 64;611	dmasound.volume_right = 64;612	custom.aud[0].audvol = dmasound.volume_left;613	custom.aud[3].audvol = 1;	/* For pseudo 14bit */614	custom.aud[1].audvol = dmasound.volume_right;615	custom.aud[2].audvol = 1;	/* For pseudo 14bit */616	dmasound.treble = 50;617}618 619static int AmiMixerIoctl(u_int cmd, u_long arg)620{621	int data;622	switch (cmd) {623	    case SOUND_MIXER_READ_DEVMASK:624		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);625	    case SOUND_MIXER_READ_RECMASK:626		    return IOCTL_OUT(arg, 0);627	    case SOUND_MIXER_READ_STEREODEVS:628		    return IOCTL_OUT(arg, SOUND_MASK_VOLUME);629	    case SOUND_MIXER_READ_VOLUME:630		    return IOCTL_OUT(arg,631			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |632			    VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);633	    case SOUND_MIXER_WRITE_VOLUME:634		    IOCTL_IN(arg, data);635		    return IOCTL_OUT(arg, dmasound_set_volume(data));636	    case SOUND_MIXER_READ_TREBLE:637		    return IOCTL_OUT(arg, dmasound.treble);638	    case SOUND_MIXER_WRITE_TREBLE:639		    IOCTL_IN(arg, data);640		    return IOCTL_OUT(arg, dmasound_set_treble(data));641	}642	return -EINVAL;643}644 645 646static int AmiWriteSqSetup(void)647{648	write_sq_block_size_half = write_sq.block_size>>1;649	write_sq_block_size_quarter = write_sq_block_size_half>>1;650	return 0;651}652 653 654static int AmiStateInfo(char *buffer, size_t space)655{656	int len = 0;657	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",658		       dmasound.volume_left);659	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",660		       dmasound.volume_right);661	if (len >= space) {662		printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;663		len = space ;664	}665	return len;666}667 668 669/*** Machine definitions *****************************************************/670 671static SETTINGS def_hard = {672	.format	= AFMT_S8,673	.stereo	= 0,674	.size	= 8,675	.speed	= 8000676} ;677 678static SETTINGS def_soft = {679	.format	= AFMT_U8,680	.stereo	= 0,681	.size	= 8,682	.speed	= 8000683} ;684 685static MACHINE machAmiga = {686	.name		= "Amiga",687	.name2		= "AMIGA",688	.owner		= THIS_MODULE,689	.dma_alloc	= AmiAlloc,690	.dma_free	= AmiFree,691	.irqinit	= AmiIrqInit,692#ifdef MODULE693	.irqcleanup	= AmiIrqCleanUp,694#endif /* MODULE */695	.init		= AmiInit,696	.silence	= AmiSilence,697	.setFormat	= AmiSetFormat,698	.setVolume	= AmiSetVolume,699	.setTreble	= AmiSetTreble,700	.play		= AmiPlay,701	.mixer_init	= AmiMixerInit,702	.mixer_ioctl	= AmiMixerIoctl,703	.write_sq_setup	= AmiWriteSqSetup,704	.state_info	= AmiStateInfo,705	.min_dsp_speed	= 8000,706	.version	= ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),707	.hardware_afmts	= (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */708	.capabilities	= DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */709};710 711 712/*** Config & Setup **********************************************************/713 714 715static int __init amiga_audio_probe(struct platform_device *pdev)716{717	dmasound.mach = machAmiga;718	dmasound.mach.default_hard = def_hard ;719	dmasound.mach.default_soft = def_soft ;720	return dmasound_init();721}722 723static void __exit amiga_audio_remove(struct platform_device *pdev)724{725	dmasound_deinit();726}727 728/*729 * amiga_audio_remove() lives in .exit.text. For drivers registered via730 * module_platform_driver_probe() this is ok because they cannot get unbound at731 * runtime. So mark the driver struct with __refdata to prevent modpost732 * triggering a section mismatch warning.733 */734static struct platform_driver amiga_audio_driver __refdata = {735	.remove_new = __exit_p(amiga_audio_remove),736	.driver = {737		.name	= "amiga-audio",738	},739};740 741module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);742 743MODULE_DESCRIPTION("Amiga Paula DMA Sound Driver");744MODULE_LICENSE("GPL");745MODULE_ALIAS("platform:amiga-audio");746