Path: blob/master/sound/oss/dmasound/dmasound_paula.c
10817 views
/*1* linux/sound/oss/dmasound/dmasound_paula.c2*3* Amiga `Paula' DMA Sound Driver4*5* See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits6* prior to 28/01/20017*8* 28/01/2001 [0.1] Iain Sandoe9* - added versioning10* - put in and populated the hardware_afmts field.11* [0.2] - put in SNDCTL_DSP_GETCAPS value.12* [0.3] - put in constraint on state buffer usage.13* [0.4] - put in default hard/soft settings14*/151617#include <linux/module.h>18#include <linux/mm.h>19#include <linux/init.h>20#include <linux/ioport.h>21#include <linux/soundcard.h>22#include <linux/interrupt.h>23#include <linux/platform_device.h>2425#include <asm/uaccess.h>26#include <asm/setup.h>27#include <asm/amigahw.h>28#include <asm/amigaints.h>29#include <asm/machdep.h>3031#include "dmasound.h"3233#define DMASOUND_PAULA_REVISION 034#define DMASOUND_PAULA_EDITION 43536#define custom amiga_custom37/*38* The minimum period for audio depends on htotal (for OCS/ECS/AGA)39* (Imported from arch/m68k/amiga/amisound.c)40*/4142extern volatile u_short amiga_audio_min_period;434445/*46* amiga_mksound() should be able to restore the period after beeping47* (Imported from arch/m68k/amiga/amisound.c)48*/4950extern u_short amiga_audio_period;515253/*54* Audio DMA masks55*/5657#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)58#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)59#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)606162/*63* Helper pointers for 16(14)-bit sound64*/6566static int write_sq_block_size_half, write_sq_block_size_quarter;676869/*** Low level stuff *********************************************************/707172static void *AmiAlloc(unsigned int size, gfp_t flags);73static void AmiFree(void *obj, unsigned int size);74static int AmiIrqInit(void);75#ifdef MODULE76static void AmiIrqCleanUp(void);77#endif78static void AmiSilence(void);79static void AmiInit(void);80static int AmiSetFormat(int format);81static int AmiSetVolume(int volume);82static int AmiSetTreble(int treble);83static void AmiPlayNextFrame(int index);84static void AmiPlay(void);85static irqreturn_t AmiInterrupt(int irq, void *dummy);8687#ifdef CONFIG_HEARTBEAT8889/*90* Heartbeat interferes with sound since the 7 kHz low-pass filter and the91* power LED are controlled by the same line.92*/9394static void (*saved_heartbeat)(int) = NULL;9596static inline void disable_heartbeat(void)97{98if (mach_heartbeat) {99saved_heartbeat = mach_heartbeat;100mach_heartbeat = NULL;101}102AmiSetTreble(dmasound.treble);103}104105static inline void enable_heartbeat(void)106{107if (saved_heartbeat)108mach_heartbeat = saved_heartbeat;109}110#else /* !CONFIG_HEARTBEAT */111#define disable_heartbeat() do { } while (0)112#define enable_heartbeat() do { } while (0)113#endif /* !CONFIG_HEARTBEAT */114115116/*** Mid level stuff *********************************************************/117118static void AmiMixerInit(void);119static int AmiMixerIoctl(u_int cmd, u_long arg);120static int AmiWriteSqSetup(void);121static int AmiStateInfo(char *buffer, size_t space);122123124/*** Translations ************************************************************/125126/* ++TeSche: radically changed for new expanding purposes...127*128* These two routines now deal with copying/expanding/translating the samples129* from user space into our buffer at the right frequency. They take care about130* how much data there's actually to read, how much buffer space there is and131* to convert samples into the right frequency/encoding. They will only work on132* complete samples so it may happen they leave some bytes in the input stream133* if the user didn't write a multiple of the current sample size. They both134* return the number of bytes they've used from both streams so you may detect135* such a situation. Luckily all programs should be able to cope with that.136*137* I think I've optimized anything as far as one can do in plain C, all138* variables should fit in registers and the loops are really short. There's139* one loop for every possible situation. Writing a more generalized and thus140* parameterized loop would only produce slower code. Feel free to optimize141* this in assembler if you like. :)142*143* I think these routines belong here because they're not yet really hardware144* independent, especially the fact that the Falcon can play 16bit samples145* only in stereo is hardcoded in both of them!146*147* ++geert: split in even more functions (one per format)148*/149150151/*152* Native format153*/154155static ssize_t ami_ct_s8(const u_char __user *userPtr, size_t userCount,156u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)157{158ssize_t count, used;159160if (!dmasound.soft.stereo) {161void *p = &frame[*frameUsed];162count = min_t(unsigned long, userCount, frameLeft) & ~1;163used = count;164if (copy_from_user(p, userPtr, count))165return -EFAULT;166} else {167u_char *left = &frame[*frameUsed>>1];168u_char *right = left+write_sq_block_size_half;169count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;170used = count*2;171while (count > 0) {172if (get_user(*left++, userPtr++)173|| get_user(*right++, userPtr++))174return -EFAULT;175count--;176}177}178*frameUsed += used;179return used;180}181182183/*184* Copy and convert 8 bit data185*/186187#define GENERATE_AMI_CT8(funcname, convsample) \188static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \189u_char frame[], ssize_t *frameUsed, \190ssize_t frameLeft) \191{ \192ssize_t count, used; \193\194if (!dmasound.soft.stereo) { \195u_char *p = &frame[*frameUsed]; \196count = min_t(size_t, userCount, frameLeft) & ~1; \197used = count; \198while (count > 0) { \199u_char data; \200if (get_user(data, userPtr++)) \201return -EFAULT; \202*p++ = convsample(data); \203count--; \204} \205} else { \206u_char *left = &frame[*frameUsed>>1]; \207u_char *right = left+write_sq_block_size_half; \208count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \209used = count*2; \210while (count > 0) { \211u_char data; \212if (get_user(data, userPtr++)) \213return -EFAULT; \214*left++ = convsample(data); \215if (get_user(data, userPtr++)) \216return -EFAULT; \217*right++ = convsample(data); \218count--; \219} \220} \221*frameUsed += used; \222return used; \223}224225#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)])226#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)])227#define AMI_CT_U8(x) ((x) ^ 0x80)228229GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)230GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)231GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)232233234/*235* Copy and convert 16 bit data236*/237238#define GENERATE_AMI_CT_16(funcname, convsample) \239static ssize_t funcname(const u_char __user *userPtr, size_t userCount, \240u_char frame[], ssize_t *frameUsed, \241ssize_t frameLeft) \242{ \243const u_short __user *ptr = (const u_short __user *)userPtr; \244ssize_t count, used; \245u_short data; \246\247if (!dmasound.soft.stereo) { \248u_char *high = &frame[*frameUsed>>1]; \249u_char *low = high+write_sq_block_size_half; \250count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \251used = count*2; \252while (count > 0) { \253if (get_user(data, ptr++)) \254return -EFAULT; \255data = convsample(data); \256*high++ = data>>8; \257*low++ = (data>>2) & 0x3f; \258count--; \259} \260} else { \261u_char *lefth = &frame[*frameUsed>>2]; \262u_char *leftl = lefth+write_sq_block_size_quarter; \263u_char *righth = lefth+write_sq_block_size_half; \264u_char *rightl = righth+write_sq_block_size_quarter; \265count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \266used = count*4; \267while (count > 0) { \268if (get_user(data, ptr++)) \269return -EFAULT; \270data = convsample(data); \271*lefth++ = data>>8; \272*leftl++ = (data>>2) & 0x3f; \273if (get_user(data, ptr++)) \274return -EFAULT; \275data = convsample(data); \276*righth++ = data>>8; \277*rightl++ = (data>>2) & 0x3f; \278count--; \279} \280} \281*frameUsed += used; \282return used; \283}284285#define AMI_CT_S16BE(x) (x)286#define AMI_CT_U16BE(x) ((x) ^ 0x8000)287#define AMI_CT_S16LE(x) (le2be16((x)))288#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000)289290GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)291GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)292GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)293GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)294295296static TRANS transAmiga = {297.ct_ulaw = ami_ct_ulaw,298.ct_alaw = ami_ct_alaw,299.ct_s8 = ami_ct_s8,300.ct_u8 = ami_ct_u8,301.ct_s16be = ami_ct_s16be,302.ct_u16be = ami_ct_u16be,303.ct_s16le = ami_ct_s16le,304.ct_u16le = ami_ct_u16le,305};306307/*** Low level stuff *********************************************************/308309static inline void StopDMA(void)310{311custom.aud[0].audvol = custom.aud[1].audvol = 0;312custom.aud[2].audvol = custom.aud[3].audvol = 0;313custom.dmacon = AMI_AUDIO_OFF;314enable_heartbeat();315}316317static void *AmiAlloc(unsigned int size, gfp_t flags)318{319return amiga_chip_alloc((long)size, "dmasound [Paula]");320}321322static void AmiFree(void *obj, unsigned int size)323{324amiga_chip_free (obj);325}326327static int __init AmiIrqInit(void)328{329/* turn off DMA for audio channels */330StopDMA();331332/* Register interrupt handler. */333if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",334AmiInterrupt))335return 0;336return 1;337}338339#ifdef MODULE340static void AmiIrqCleanUp(void)341{342/* turn off DMA for audio channels */343StopDMA();344/* release the interrupt */345free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);346}347#endif /* MODULE */348349static void AmiSilence(void)350{351/* turn off DMA for audio channels */352StopDMA();353}354355356static void AmiInit(void)357{358int period, i;359360AmiSilence();361362if (dmasound.soft.speed)363period = amiga_colorclock/dmasound.soft.speed-1;364else365period = amiga_audio_min_period;366dmasound.hard = dmasound.soft;367dmasound.trans_write = &transAmiga;368369if (period < amiga_audio_min_period) {370/* we would need to squeeze the sound, but we won't do that */371period = amiga_audio_min_period;372} else if (period > 65535) {373period = 65535;374}375dmasound.hard.speed = amiga_colorclock/(period+1);376377for (i = 0; i < 4; i++)378custom.aud[i].audper = period;379amiga_audio_period = period;380}381382383static int AmiSetFormat(int format)384{385int size;386387/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */388389switch (format) {390case AFMT_QUERY:391return dmasound.soft.format;392case AFMT_MU_LAW:393case AFMT_A_LAW:394case AFMT_U8:395case AFMT_S8:396size = 8;397break;398case AFMT_S16_BE:399case AFMT_U16_BE:400case AFMT_S16_LE:401case AFMT_U16_LE:402size = 16;403break;404default: /* :-) */405size = 8;406format = AFMT_S8;407}408409dmasound.soft.format = format;410dmasound.soft.size = size;411if (dmasound.minDev == SND_DEV_DSP) {412dmasound.dsp.format = format;413dmasound.dsp.size = dmasound.soft.size;414}415AmiInit();416417return format;418}419420421#define VOLUME_VOXWARE_TO_AMI(v) \422(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)423#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)424425static int AmiSetVolume(int volume)426{427dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);428custom.aud[0].audvol = dmasound.volume_left;429dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);430custom.aud[1].audvol = dmasound.volume_right;431if (dmasound.hard.size == 16) {432if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {433custom.aud[2].audvol = 1;434custom.aud[3].audvol = 1;435} else {436custom.aud[2].audvol = 0;437custom.aud[3].audvol = 0;438}439}440return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |441(VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);442}443444static int AmiSetTreble(int treble)445{446dmasound.treble = treble;447if (treble < 50)448ciaa.pra &= ~0x02;449else450ciaa.pra |= 0x02;451return treble;452}453454455#define AMI_PLAY_LOADED 1456#define AMI_PLAY_PLAYING 2457#define AMI_PLAY_MASK 3458459460static void AmiPlayNextFrame(int index)461{462u_char *start, *ch0, *ch1, *ch2, *ch3;463u_long size;464465/* used by AmiPlay() if all doubts whether there really is something466* to be played are already wiped out.467*/468start = write_sq.buffers[write_sq.front];469size = (write_sq.count == index ? write_sq.rear_size470: write_sq.block_size)>>1;471472if (dmasound.hard.stereo) {473ch0 = start;474ch1 = start+write_sq_block_size_half;475size >>= 1;476} else {477ch0 = start;478ch1 = start;479}480481disable_heartbeat();482custom.aud[0].audvol = dmasound.volume_left;483custom.aud[1].audvol = dmasound.volume_right;484if (dmasound.hard.size == 8) {485custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);486custom.aud[0].audlen = size;487custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);488custom.aud[1].audlen = size;489custom.dmacon = AMI_AUDIO_8;490} else {491size >>= 1;492custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);493custom.aud[0].audlen = size;494custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);495custom.aud[1].audlen = size;496if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {497/* We can play pseudo 14-bit only with the maximum volume */498ch3 = ch0+write_sq_block_size_quarter;499ch2 = ch1+write_sq_block_size_quarter;500custom.aud[2].audvol = 1; /* we are being affected by the beeps */501custom.aud[3].audvol = 1; /* restoring volume here helps a bit */502custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);503custom.aud[2].audlen = size;504custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);505custom.aud[3].audlen = size;506custom.dmacon = AMI_AUDIO_14;507} else {508custom.aud[2].audvol = 0;509custom.aud[3].audvol = 0;510custom.dmacon = AMI_AUDIO_8;511}512}513write_sq.front = (write_sq.front+1) % write_sq.max_count;514write_sq.active |= AMI_PLAY_LOADED;515}516517518static void AmiPlay(void)519{520int minframes = 1;521522custom.intena = IF_AUD0;523524if (write_sq.active & AMI_PLAY_LOADED) {525/* There's already a frame loaded */526custom.intena = IF_SETCLR | IF_AUD0;527return;528}529530if (write_sq.active & AMI_PLAY_PLAYING)531/* Increase threshold: frame 1 is already being played */532minframes = 2;533534if (write_sq.count < minframes) {535/* Nothing to do */536custom.intena = IF_SETCLR | IF_AUD0;537return;538}539540if (write_sq.count <= minframes &&541write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {542/* hmmm, the only existing frame is not543* yet filled and we're not syncing?544*/545custom.intena = IF_SETCLR | IF_AUD0;546return;547}548549AmiPlayNextFrame(minframes);550551custom.intena = IF_SETCLR | IF_AUD0;552}553554555static irqreturn_t AmiInterrupt(int irq, void *dummy)556{557int minframes = 1;558559custom.intena = IF_AUD0;560561if (!write_sq.active) {562/* Playing was interrupted and sq_reset() has already cleared563* the sq variables, so better don't do anything here.564*/565WAKE_UP(write_sq.sync_queue);566return IRQ_HANDLED;567}568569if (write_sq.active & AMI_PLAY_PLAYING) {570/* We've just finished a frame */571write_sq.count--;572WAKE_UP(write_sq.action_queue);573}574575if (write_sq.active & AMI_PLAY_LOADED)576/* Increase threshold: frame 1 is already being played */577minframes = 2;578579/* Shift the flags */580write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;581582if (!write_sq.active)583/* No frame is playing, disable audio DMA */584StopDMA();585586custom.intena = IF_SETCLR | IF_AUD0;587588if (write_sq.count >= minframes)589/* Try to play the next frame */590AmiPlay();591592if (!write_sq.active)593/* Nothing to play anymore.594Wake up a process waiting for audio output to drain. */595WAKE_UP(write_sq.sync_queue);596return IRQ_HANDLED;597}598599/*** Mid level stuff *********************************************************/600601602/*603* /dev/mixer abstraction604*/605606static void __init AmiMixerInit(void)607{608dmasound.volume_left = 64;609dmasound.volume_right = 64;610custom.aud[0].audvol = dmasound.volume_left;611custom.aud[3].audvol = 1; /* For pseudo 14bit */612custom.aud[1].audvol = dmasound.volume_right;613custom.aud[2].audvol = 1; /* For pseudo 14bit */614dmasound.treble = 50;615}616617static int AmiMixerIoctl(u_int cmd, u_long arg)618{619int data;620switch (cmd) {621case SOUND_MIXER_READ_DEVMASK:622return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);623case SOUND_MIXER_READ_RECMASK:624return IOCTL_OUT(arg, 0);625case SOUND_MIXER_READ_STEREODEVS:626return IOCTL_OUT(arg, SOUND_MASK_VOLUME);627case SOUND_MIXER_READ_VOLUME:628return IOCTL_OUT(arg,629VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |630VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);631case SOUND_MIXER_WRITE_VOLUME:632IOCTL_IN(arg, data);633return IOCTL_OUT(arg, dmasound_set_volume(data));634case SOUND_MIXER_READ_TREBLE:635return IOCTL_OUT(arg, dmasound.treble);636case SOUND_MIXER_WRITE_TREBLE:637IOCTL_IN(arg, data);638return IOCTL_OUT(arg, dmasound_set_treble(data));639}640return -EINVAL;641}642643644static int AmiWriteSqSetup(void)645{646write_sq_block_size_half = write_sq.block_size>>1;647write_sq_block_size_quarter = write_sq_block_size_half>>1;648return 0;649}650651652static int AmiStateInfo(char *buffer, size_t space)653{654int len = 0;655len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",656dmasound.volume_left);657len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",658dmasound.volume_right);659if (len >= space) {660printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;661len = space ;662}663return len;664}665666667/*** Machine definitions *****************************************************/668669static SETTINGS def_hard = {670.format = AFMT_S8,671.stereo = 0,672.size = 8,673.speed = 8000674} ;675676static SETTINGS def_soft = {677.format = AFMT_U8,678.stereo = 0,679.size = 8,680.speed = 8000681} ;682683static MACHINE machAmiga = {684.name = "Amiga",685.name2 = "AMIGA",686.owner = THIS_MODULE,687.dma_alloc = AmiAlloc,688.dma_free = AmiFree,689.irqinit = AmiIrqInit,690#ifdef MODULE691.irqcleanup = AmiIrqCleanUp,692#endif /* MODULE */693.init = AmiInit,694.silence = AmiSilence,695.setFormat = AmiSetFormat,696.setVolume = AmiSetVolume,697.setTreble = AmiSetTreble,698.play = AmiPlay,699.mixer_init = AmiMixerInit,700.mixer_ioctl = AmiMixerIoctl,701.write_sq_setup = AmiWriteSqSetup,702.state_info = AmiStateInfo,703.min_dsp_speed = 8000,704.version = ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),705.hardware_afmts = (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */706.capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */707};708709710/*** Config & Setup **********************************************************/711712713static int __init amiga_audio_probe(struct platform_device *pdev)714{715dmasound.mach = machAmiga;716dmasound.mach.default_hard = def_hard ;717dmasound.mach.default_soft = def_soft ;718return dmasound_init();719}720721static int __exit amiga_audio_remove(struct platform_device *pdev)722{723dmasound_deinit();724return 0;725}726727static struct platform_driver amiga_audio_driver = {728.remove = __exit_p(amiga_audio_remove),729.driver = {730.name = "amiga-audio",731.owner = THIS_MODULE,732},733};734735static int __init amiga_audio_init(void)736{737return platform_driver_probe(&amiga_audio_driver, amiga_audio_probe);738}739740module_init(amiga_audio_init);741742static void __exit amiga_audio_exit(void)743{744platform_driver_unregister(&amiga_audio_driver);745}746747module_exit(amiga_audio_exit);748749MODULE_LICENSE("GPL");750MODULE_ALIAS("platform:amiga-audio");751752753