From: Adrian M. <ad...@mc...> - 2002-01-27 01:00:09
|
C++ style (actually C99 compliant :->) comments removed/replaced: --- main.c Sat Jan 26 11:38:03 2002 +++ aica.c Sun Jan 27 00:46:10 2002 @@ -32,7 +32,7 @@ #include <asm/semaphore.h> #include <asm/dc_sysasic.h> #include "../sound_config.h" -#include "arm7.h" //static array of ARM 7 code +#include "arm7.h" /* Command values */ #define AICA_CMD_KICK 0x80000000 @@ -177,17 +177,17 @@ } + +/* Stop the ARM7 processor and clear registers + for all channels */ void spu_disable() { int i; - - /* Stop the ARM processor */ spu_write_wait(); uint32_t regval = readl(0xa0702c00); regval |= 1; spu_write_wait(); writel(regval, 0xa0702c00); - //close down all channels for (i = 0; i < 64; i++) { spu_write_wait(); regval = readl(0xa0700000 + (i * 0x80)); @@ -200,26 +200,19 @@ } +/* Halt the sound processor, + clear the memory, + load some default ARM7 code, + and then restart ARM7 +*/ int spu_init() { - /* Stop the ARM */ + spu_disable(); - - /* Clear out sound RAM */ spu_memset(0, 0, 0x200000 / 4); - - /* Load a default "program" into the SPU that just executes - an infinite loop */ *(uint32_t *) 0xa0800000 = 0xea000002; - - /* Start the SPU again */ spu_enable(); - - // Wait a little schedule(); - - - return 0; } @@ -251,52 +244,48 @@ typedef struct aica_dev { struct aica_dev *next_dev; - int channel; //which one are we using here? - int audio_minor; //device number - int mixer_minor; //device number - wait_queue_head_t open_wait; //wait queue - int last_write_length; //length of data last written to device - int sformat; //format of data being written + int channel; /* which one are we using here? */ + int audio_minor; /* device number */ + int mixer_minor; /* device number */ + wait_queue_head_t open_wait; /* wait queue */ + int last_write_length; /* length of data last written to device */ + int sformat; /* format of data being written */ } aica_dev_t; -//static linked list of all open devices +/* List of all open devices */ static aica_dev_t *aica_dev_list; +/* open the device file, locking AICA registers as we go */ + static int aica_audio_open(struct inode *inode, struct file *file) { aica_dev_t *devc; dev_t minor = MINOR(inode->i_rdev); MOD_INC_USE_COUNT; - /* open the channel - * locking the memory as appropriate - */ + devc = aica_dev_list; if (down_interruptible(&dsp_mutex)) return -ERESTARTSYS; - - //Lock channels 1 & 2 + request_mem_region(0xa0700000, 0x100, "AICA Channels"); - //sample type - based on which device has been opened chanh = kmalloc(sizeof(aica_channel), GFP_KERNEL); if (chanh == NULL) return -ENOMEM; chanh->flags = 0; if ((minor & 0xF) == SND_DEV_DSP) { - chanh->sfmt = SM_8BIT; //AICA format - devc->sformat = AFMT_U8; //OSS default format for this device + chanh->sfmt = SM_8BIT; + devc->sformat = AFMT_U8; } else if ((minor & 0xF) == SND_DEV_DSP16) { chanh->sfmt = SM_16BIT; devc->sformat = AFMT_S16_LE; } else { - //total failure! DEBGM("Attempting to open an unsupported device file\n"); - //unlock channel release_mem_region(0xa0700000, 0x100); up(&dsp_mutex); MOD_DEC_USE_COUNT; @@ -306,7 +295,6 @@ devc->last_write_length = 0; file->private_data = devc; - //Start AICA spu_disable(); spu_memset(0, 0, 0x31000); spu_memload(0, bin_arm7, sizeof(bin_arm7)); @@ -317,12 +305,15 @@ chanh->vol = left_volume; chanh->pan = 0x80; chanh->pos = 0; - chanh->freq = 8000; //8k is defined as the OSS default + chanh->freq = 8000; total_count = 0; currentpoint = 0; return 0; } + +/* Release device, waiting for queued audio to play out */ + static int aica_audio_release(struct inode *inode, struct file *file) { aica_dev_t *devc = (aica_dev_t *) (file->private_data); @@ -335,8 +326,6 @@ playpoint = readl(0xa0810004 + 4); if ((playpoint * samplelength) > currentpoint) { for (i = 0; i < (HZ * 2); i++) { - //wait comes back to start of buffer - interruptible_sleep_on_timeout(& (devc-> open_wait), @@ -362,7 +351,6 @@ break; } } - //reached the end of the road spu_disable(); if (BUFFER) @@ -386,13 +374,13 @@ return 0; } + +/* Perform stereo seperation for 16- and 8-bit samples */ static int aica_audio_process_stereo(int *count_out, int *count) { - - //second channel has largely the same attributes as first int z, y, x, w; *count_out = *count / 2; - y = *count_out; //only need to read this once + y = *count_out; BUFFER2 = kmalloc(y, GFP_KERNEL); if (BUFFER2 == NULL) return -1; @@ -424,10 +412,12 @@ return 0; } - +/* Block if buffer full until playing has freed + up necessary space. Sleep while waiting for + low smaple rates, but lock processor for + demaning samples */ inline static int aica_audio_tidy_buffers(aica_dev_t * devc) { - //about to overwrite a playing buffer? int playpoint; do { if (chanh->freq < 23000) @@ -438,10 +428,8 @@ } while (((playpoint * samplelength) > currentpoint) && ((playpoint * samplelength) < (currentpoint + total_count))); - //writing past end of the buffer? if ((currentpoint + total_count) > 0x8000) { currentpoint = 0; - //check again do { if (chanh->freq < 23000) interruptible_sleep_on_timeout(& @@ -467,13 +455,11 @@ BUFFER = kmalloc(count, GFP_KERNEL); copy_from_user(BUFFER, buffer, count); int count_out = count; - //check for any format changes needed samplelength = 1; if (chanh->sfmt == SM_16BIT) samplelength = 2; else if (devc->sformat == AFMT_U8) convert_u8tos8(BUFFER, count); - //process stereo if required if (stereo) { if (aica_audio_process_stereo(&count_out, &count) != 0) return -1; @@ -515,9 +501,11 @@ int newdata, left, right, s, m; audio_buf_info info; switch (cmd) { + case SNDCTL_DSP_GETCAPS: put_user(DSP_CAP_TRIGGER | DSP_CAP_REALTIME, (int *) arg); return 0; + case SNDCTL_DSP_SETFMT: get_user(newdata, (int *) arg); switch (newdata) { @@ -540,16 +528,18 @@ default: devc->sformat = AFMT_U8; chanh->sfmt = SM_8BIT; - //have set a default format - //but should return an error + /* have set a default format + but should return an error */ return -ENOTTY; } put_user(devc->sformat, (int *) arg); return 0; + case SNDCTL_DSP_GETFMTS: newdata = AFMT_S8 | AFMT_S16_LE | AFMT_IMA_ADPCM; put_user(newdata, (int *) arg); return 0; + case SNDCTL_DSP_SPEED: get_user(newdata, (int *) arg); if (newdata < 10000) { @@ -570,16 +560,18 @@ chanh->freq = 44100; put_user(chanh->freq, (int *) arg); return 0; + case SNDCTL_DSP_RESET: chn_halt(); - //clear memory spu_memset(0x21000, 0, 0x8000); spu_memset(0x11000, 0, 0x8000); currentpoint = 0; return 0; + case SNDCTL_DSP_GETBLKSIZE: put_user(buffer_size, (int *) arg); return 0; + case SNDCTL_DSP_SETFRAGMENT: get_user(newdata, (int *) arg); s = newdata & 0xffff; @@ -592,8 +584,9 @@ arg = (m << 16) | s; put_user(0x010f, (int *) arg); return 0; + + /* bytes left to play */ case SNDCTL_DSP_GETODELAY: - //bytes left to play spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -603,8 +596,10 @@ newdata = currentpoint - (s * samplelength); put_user(newdata, (int *) arg); return 0; + + /* How many fragments till writing blocks? */ case SNDCTL_DSP_GETOSPACE: - //how many framents till writing blocks? + spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -615,15 +610,14 @@ info.fragments = ((0x8000 - currentpoint) + (s * samplelength)) / buffer_size; - - info.fragstotal = 0x8000 / buffer_size; //total fragments possible + info.fragstotal = 0x8000 / buffer_size; info.fragsize = buffer_size; info.bytes = info.fragments * buffer_size; copy_to_user((audio_buf_info *) arg, &info, sizeof(info)); return 0; + case SNDCTL_DSP_SYNC: case SNDCTL_DSP_POST: - //flushes sound buffer spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -631,20 +625,18 @@ currentpoint + 0x8000 - (s * samplelength); } else newdata = currentpoint - (s * samplelength); - //work out delay newdata = newdata / samplelength; - //in milliseconds + /* delay in milliseconds */ newdata = (newdata * 1000) / chanh->freq; - //sleep that time then kill sound newdata = (newdata * HZ) / 1000; interruptible_sleep_on_timeout(&(devc->open_wait), newdata); chn_halt(); - //clear memory spu_memset(0x21000, 0, 0x8000); spu_memset(0x11000, 0, 0x8000); currentpoint = 0; return 0; + case SNDCTL_DSP_STEREO: get_user(newdata, (int *) arg); if (newdata == 1) { @@ -662,20 +654,20 @@ newdata = 1; put_user(newdata, (int *) arg); return 0; - //Supported Mixer ioctls + + /* Supported Mixer ioctls */ case SOUND_MIXER_READ_DEVMASK: newdata = SOUND_MASK_PCM | SOUND_MASK_VOLUME; put_user(newdata, (int *) arg); return 0; + case SOUND_MIXER_WRITE_VOLUME: case SOUND_MIXER_WRITE_PCM: - get_user(newdata, (int *) arg); - //Stereo channel volume + get_user(newdata, (int *) arg); left = newdata & 0xff; right = (newdata & 0xff00) >> 8; left_volume = left; right_volume = right; - //rescale and set chanh->vol = (left * 255) / 100; newdata = ((right * 255) / 100) << 8; newdata = newdata | 0xff; @@ -701,7 +693,9 @@ }; -//Mixer code + +/* Mixer code + very basic currently */ static int aica_mixer_open(struct inode *inode, struct file *file) { @@ -723,19 +717,19 @@ return -1; int newdata, left, right; switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: newdata = SOUND_MASK_PCM | SOUND_MASK_VOLUME; put_user(newdata, (int *) arg); return 0; + case SOUND_MIXER_WRITE_VOLUME: case SOUND_MIXER_WRITE_PCM: get_user(newdata, (int *) arg); - //Stereo channel volume left = newdata & 0xff; right = (newdata & 0xff00) >> 8; left_volume = left; right_volume = right; - //rescale and set chanh->vol = (left * 255) / 100; newdata = ((right * 255) / 100) << 8; newdata = newdata | 0xff; @@ -757,11 +751,18 @@ ioctl:aica_mixer_ioctl, }; + + +/* Create holder for device, + together with wait queue + and then register device + with underlying OSS code +*/ + static int __init attach_aica(void) { printk - ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre14\n"); - //initialise data holder for device + ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre14\n"); aica_dev_t *devc = NULL; sema_init(&dsp_mutex, 1); int err = -ENOMEM; @@ -770,17 +771,13 @@ if (devc == NULL) goto fail0; - //initialise wait queue init_waitqueue_head(&devc->open_wait); - //register device drivers - //register sound_dsp with OSS devc->audio_minor = register_sound_dsp(&aica_audio_fops, -1); if (devc->audio_minor < 0) { DEBGM("attach_aica: register_sound_dsp error %d\n", err); err = devc->audio_minor; goto faildsp; } - //register mixer with OSS devc->mixer_minor = register_sound_mixer(&aica_mixer_fops, -1); if (devc->mixer_minor < 0) { DEBGM("attach_aica: register_sound_mixer error %d\n", err); @@ -815,8 +812,6 @@ static int __exit unload_aica(void) { - //walk through the list - //deallocating the lot aica_dev_t *devc; aica_dev_t *devcp; devcp = aica_dev_list; @@ -835,6 +830,12 @@ return 0; } + + +/* Lock iomem and initialise + ARM7 processor +*/ + static int __init init_aica(void) { @@ -842,23 +843,19 @@ BUFFER2 = NULL; BUFFERL = NULL; BUFFERR = NULL; - /* take ownership of register that controls ARM */ if (request_mem_region(0xa0702c00, 4, "AICA ARM control") == NULL) { DEBGM ("Could not take ownership of SPU control register\n"); return -ENODEV; } -/* take ownership of sound memory */ if (request_mem_region(0xa0800000, 0x200000, "AICA Sound RAM") == NULL) { DEBGM("Could not take ownership of sound RAM\n"); return -ENOMEM; } - //Basic spu set up spu_init(); - //now create the devfs files etc if (attach_aica()) { DEBGM("Failed to initialise AICA sound driver\n"); return -EINVAL; @@ -869,19 +866,14 @@ static void __exit exit_aica(void) { -//set low bit of register to 1 DEBGM("AICA sound driver being unloaded...\n"); - if (unload_aica()) DEBGM("Error in unloading AICA\n"); - spu_init(); //revert to neutral state - + spu_init(); -/* //release the SPU control register */ release_mem_region(0xa0702c00, 4); - //release the sound memory release_mem_region(0xa0800000, 0x200000); } |