|
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);
}
|