diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /drivers/sbus/audio | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'drivers/sbus/audio')
-rw-r--r-- | drivers/sbus/audio/amd7930.c | 13 | ||||
-rw-r--r-- | drivers/sbus/audio/audio.c | 682 | ||||
-rw-r--r-- | drivers/sbus/audio/audio.h | 384 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4215.h | 4 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4231.c | 1453 | ||||
-rw-r--r-- | drivers/sbus/audio/cs4231.h | 190 |
6 files changed, 1649 insertions, 1077 deletions
diff --git a/drivers/sbus/audio/amd7930.c b/drivers/sbus/audio/amd7930.c index 6e54cbf79..fb87bbe6f 100644 --- a/drivers/sbus/audio/amd7930.c +++ b/drivers/sbus/audio/amd7930.c @@ -29,7 +29,7 @@ #include <asm/io.h> #include <asm/sbus.h> -#include "audio.h" +#include <asm/audioio.h> #include "amd7930.h" #define MAX_DRIVERS 1 @@ -342,7 +342,6 @@ static void fill_D_xmit_fifo(struct amd7930_info *info) static void transceive_Dchannel(struct amd7930_info *info) { __u8 dummy; - int lbrp=0; /* Last Byte of Received Packet (LBRP) */ #define D_XMIT_ERRORS (AMR_DER_COLLISION | AMR_DER_UNRN) #define D_RECV_ERRORS (AMR_DER_RABRT | AMR_DER_RFRAME | AMR_DER_FCS | \ @@ -433,7 +432,7 @@ static void transceive_Bchannel(struct amd7930_channel *channel, channel->output_count = 0; if (channel->output_callback) (*channel->output_callback) - (channel->output_callback_arg); + (channel->output_callback_arg,1); } } else { *io_reg = channel->xmit_idle_char; @@ -631,9 +630,9 @@ static void amd7930_stop_input(struct sparcaudio_driver *drv) static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv, audio_device_t * audinfo) { - strncpy(audinfo->name, "amd7930", sizeof(audinfo->name) - 1); - strncpy(audinfo->version, "x", sizeof(audinfo->version) - 1); - strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1); + strncpy(audinfo->name, "SUNW,am79c30", sizeof(audinfo->name) - 1); + strncpy(audinfo->version, "a", sizeof(audinfo->version) - 1); + strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1); } static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) @@ -1341,6 +1340,8 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node, info->Bc.output_count = info->Bc.input_count = 0; info->ints_on = 1; /* force disable below */ + drv->dev = sdev; + /* Map the registers into memory. */ prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); if (sbus && sdev) diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index ba9563449..ab14e2767 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -2,6 +2,12 @@ * drivers/sbus/audio/audio.c * * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) + * Copyright (C) 1997 Derrick J. Brashear (shadow@dementia.org) + * Copyright (C) 1997 Brent Baccala (baccala@freesoft.org) + * + * Mixer code adapted from code contributed by and + * Copyright (C) 1998 Michael Mraka (michael@fi.muni.cz) + * * * This is the audio midlayer that sits between the VFS character * devices and the low-level audio hardware device drivers. @@ -21,8 +27,9 @@ #include <linux/init.h> #include <linux/soundcard.h> #include <asm/uaccess.h> +#include <asm/pgtable.h> -#include "audio.h" +#include <asm/audioio.h> /* @@ -47,17 +54,18 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv) /* Setup the circular queues of output and input buffers * * Each buffer is a single page, but output buffers might - * be partially filled (by a write with count < PAGE_SIZE), + * be partially filled (by a write with count < 4096), * so each output buffer also has a paired output size. * * Input buffers, on the other hand, always fill completely, - * so we don't need input counts - each contains PAGE_SIZE + * so we don't need input counts - each contains 4096 * bytes of audio data. * * TODO: Make number of input/output buffers tunable parameters */ drv->num_output_buffers = 32; + drv->playing_count = 0; drv->output_front = 0; drv->output_rear = 0; drv->output_count = 0; @@ -74,6 +82,7 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv) /* Setup the circular queue of input buffers. */ drv->num_input_buffers = 32; + drv->recording_count = 0; drv->input_front = 0; drv->input_rear = 0; drv->input_count = 0; @@ -143,28 +152,25 @@ int unregister_sparcaudio_driver(struct sparcaudio_driver *drv) return 0; } -static void sparcaudio_output_done_task(void * arg) +void sparcaudio_output_done(struct sparcaudio_driver * drv, int reclaim) { - struct sparcaudio_driver *drv = (struct sparcaudio_driver *)arg; - unsigned long flags; + /* Reclaim a buffer unless it's still in the DMA pipe */ + if (reclaim) { + if (drv->output_count > 0) + drv->output_count--; + else + if (drv->playing_count > 0) + drv->playing_count--; + } else + drv->playing_count++; - save_and_cli(flags); - drv->ops->start_output(drv, - drv->output_buffers[drv->output_front], - drv->output_sizes[drv->output_front]); - drv->output_active = 1; - restore_flags(flags); -} - -void sparcaudio_output_done(struct sparcaudio_driver * drv) -{ /* Point the queue after the "done" buffer. */ drv->output_size -= drv->output_sizes[drv->output_front]; drv->output_front = (drv->output_front + 1) % drv->num_output_buffers; - drv->output_count--; /* If the output queue is empty, shutdown the driver. */ if (drv->output_count == 0) { + if (drv->playing_count == 0) { /* Stop the lowlevel driver from outputing. */ drv->ops->stop_output(drv); drv->output_active = 0; @@ -173,19 +179,17 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv) wake_up_interruptible(&drv->output_write_wait); wake_up_interruptible(&drv->output_drain_wait); return; + } } - /* Otherwise, queue a task to give the driver the next buffer. */ - drv->tqueue.next = NULL; - drv->tqueue.sync = 0; - drv->tqueue.routine = sparcaudio_output_done_task; - drv->tqueue.data = drv; + /* If we got back a buffer, see if anyone wants to write to it */ + if (reclaim || ((drv->output_count + drv->playing_count) + < drv->num_output_buffers)) + wake_up_interruptible(&drv->output_write_wait); + + drv->ops->start_output(drv, drv->output_buffers[drv->output_front], + drv->output_sizes[drv->output_front]); - queue_task(&drv->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); - - /* Wake up any tasks that are waiting. */ - wake_up_interruptible(&drv->output_write_wait); } void sparcaudio_input_done(struct sparcaudio_driver * drv) @@ -202,7 +206,7 @@ void sparcaudio_input_done(struct sparcaudio_driver * drv) } else { /* Otherwise, give the driver the next buffer. */ drv->ops->start_input(drv, drv->input_buffers[drv->input_front], - PAGE_SIZE); + 4096); } /* Wake up any tasks that are waiting. */ @@ -234,7 +238,7 @@ static ssize_t sparcaudio_read(struct file * file, return -EINTR; } - bytes_to_copy = PAGE_SIZE - driver->input_offset; + bytes_to_copy = 4096 - driver->input_offset; if (bytes_to_copy > count) bytes_to_copy = count; @@ -242,7 +246,7 @@ static ssize_t sparcaudio_read(struct file * file, bytes_to_copy, -EFAULT); driver->input_offset += bytes_to_copy; - if (driver->input_offset >= PAGE_SIZE) { + if (driver->input_offset >= 4096) { driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers; driver->input_count--; driver->input_offset = 0; @@ -263,11 +267,11 @@ static void sparcaudio_sync_output(struct sparcaudio_driver * driver) /* If the low-level driver is not active, activate it. */ save_and_cli(flags); - if (! driver->output_active) { - driver->ops->start_output(driver, - driver->output_buffers[driver->output_front], - driver->output_sizes[driver->output_front]); - driver->output_active = 1; + if ((!driver->output_active) && (driver->output_count > 0)) { + driver->ops->start_output(driver, + driver->output_buffers[driver->output_front], + driver->output_sizes[driver->output_front]); + driver->output_active = 1; } restore_flags(flags); } @@ -277,59 +281,184 @@ static ssize_t sparcaudio_write(struct file * file, const char *buf, { int bytes_written = 0, bytes_to_copy; - /* Ensure that we have something to write. */ - if (count < 1) { - sparcaudio_sync_output(driver); - return 0; - } + if (! file->f_mode & FMODE_WRITE) + return -EINVAL; /* Loop until all output is written to device. */ while (count > 0) { - /* Check to make sure that an output buffer is available. */ - /* If not, make valiant attempt */ - if (driver->output_count == driver->num_output_buffers) - sparcaudio_reorganize_buffers(driver); - - if (driver->output_count == driver->num_output_buffers) { - /* We need buffers, so... */ - sparcaudio_sync_output(driver); - interruptible_sleep_on(&driver->output_write_wait); - if (signal_pending(current)) - return bytes_written > 0 ? bytes_written : -EINTR; + /* Check to make sure that an output buffer is available. */ + /* If not, make valiant attempt */ + if (driver->num_output_buffers == + (driver->output_count + driver->playing_count)) + sparcaudio_reorganize_buffers(driver); + + if (driver->num_output_buffers == + (driver->output_count + driver->playing_count)) { + /* We need buffers, so... */ + sparcaudio_sync_output(driver); + interruptible_sleep_on(&driver->output_write_wait); + if (signal_pending(current)) + return bytes_written > 0 ? bytes_written : -EINTR; } - /* Determine how much we can copy in this iteration. */ - bytes_to_copy = count; - if (bytes_to_copy > PAGE_SIZE) - bytes_to_copy = PAGE_SIZE; + /* No buffers were freed. Go back to sleep */ + if (driver->num_output_buffers == + (driver->output_count + driver->playing_count)) + continue; + + /* Determine how much we can copy in this iteration. */ + bytes_to_copy = count; + if (bytes_to_copy > 4096) + bytes_to_copy = 4096; - copy_from_user_ret(driver->output_buffers[driver->output_rear], + copy_from_user_ret(driver->output_buffers[driver->output_rear], buf, bytes_to_copy, -EFAULT); - /* Update the queue pointers. */ - buf += bytes_to_copy; - count -= bytes_to_copy; - bytes_written += bytes_to_copy; - driver->output_sizes[driver->output_rear] = bytes_to_copy; - driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers; - driver->output_count++; - driver->output_size += bytes_to_copy; - - /* Activate the driver if more than page of data is waiting. */ - if (driver->output_size > 4096) - sparcaudio_sync_output(driver); + /* Update the queue pointers. */ + buf += bytes_to_copy; + count -= bytes_to_copy; + bytes_written += bytes_to_copy; + driver->output_sizes[driver->output_rear] = bytes_to_copy; + driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers; + driver->output_count++; + driver->output_size += bytes_to_copy; } + sparcaudio_sync_output(driver); /* Return the number of bytes written to the caller. */ return bytes_written; } +#define COPY_IN(arg, get) get_user(get, (int *)arg) +#define COPY_OUT(arg, ret) put_user(ret, (int *)arg) + +/* Add these in as new devices are supported. Belongs in audioio.h, actually */ +#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_VOLUME) +#define MONO_DEVICES (SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SPEAKER | SOUND_MASK_MIC) + +static inline int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int i = 0, j = 0; + if (_IOC_DIR(cmd) & _IOC_WRITE) { + /* For any missing routines, pretend we changed things anyhow for now */ + switch (cmd & 0xff) { + case SOUND_MIXER_VOLUME: + if (driver->ops->get_output_channels) + j = driver->ops->get_output_channels(driver); + COPY_IN(arg, i); + if (j == 1) { + i = s_to_m(i); + if (driver->ops->set_output_volume) + driver->ops->set_output_volume(driver, i * 255/100); + if (driver->ops->get_output_volume) + i = driver->ops->get_output_volume(driver); + i = m_to_s(i); + } else { + /* there should be stuff here which calculates balance and + volume on a stereo device. will do it eventually */ + i = s_to_g(i); + if (driver->ops->set_output_volume) + driver->ops->set_output_volume(driver, i * 255/100); + if (driver->ops->get_output_volume) + i = driver->ops->get_output_volume(driver); + j = s_to_b(i); + if (driver->ops->set_output_balance) + driver->ops->set_output_balance(driver, j); + if (driver->ops->get_output_balance) + j = driver->ops->get_output_balance(driver); + i = b_to_s(i,j); + } + return COPY_OUT(arg, i); + default: + /* Play like we support other things */ + return COPY_OUT(arg, i); + } + } else { + switch (cmd & 0xff) { + case SOUND_MIXER_RECSRC: + if (driver->ops->get_input_port) + i = driver->ops->get_input_port(driver); + /* only one should ever be selected */ + if (i & AUDIO_ANALOG_LOOPBACK) j = SOUND_MASK_IMIX; /* ? */ + if (i & AUDIO_CD) j = SOUND_MASK_CD; + if (i & AUDIO_LINE_IN) j = SOUND_MASK_LINE; + if (i & AUDIO_MICROPHONE) j = SOUND_MASK_MIC; + + return COPY_OUT(arg, j); + + case SOUND_MIXER_RECMASK: + if (driver->ops->get_input_ports) + i = driver->ops->get_input_ports(driver); + /* what do we support? */ + if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC; + if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE; + if (i & AUDIO_CD) j |= SOUND_MASK_CD; + if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */ + + return COPY_OUT(arg, j); + + case SOUND_MIXER_CAPS: /* mixer capabilities */ + i = SOUND_CAP_EXCL_INPUT; + return COPY_OUT(arg, i); + + case SOUND_MIXER_DEVMASK: /* all supported devices */ + case SOUND_MIXER_STEREODEVS: /* what supports stereo */ + if (driver->ops->get_input_ports) + i = driver->ops->get_input_ports(driver); + /* what do we support? */ + if (i & AUDIO_MICROPHONE) j |= SOUND_MASK_MIC; + if (i & AUDIO_LINE_IN) j |= SOUND_MASK_LINE; + if (i & AUDIO_CD) j |= SOUND_MASK_CD; + if (i & AUDIO_ANALOG_LOOPBACK) j |= SOUND_MASK_IMIX; /* ? */ + + if (driver->ops->get_output_ports) + i = driver->ops->get_output_ports(driver); + if (i & AUDIO_SPEAKER) j |= SOUND_MASK_SPEAKER; + if (i & AUDIO_HEADPHONE) j |= SOUND_MASK_LINE; /* ? */ + if (i & AUDIO_LINE_OUT) j |= SOUND_MASK_LINE; + + j |= SOUND_MASK_VOLUME; + + if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) + j &= ~(MONO_DEVICES); + return COPY_OUT(arg, j); + + case SOUND_MIXER_VOLUME: + if (driver->ops->get_output_channels) + j = driver->ops->get_output_channels(driver); + if (j == 1) { + if (driver->ops->get_output_volume) + i = driver->ops->get_output_volume(driver); + i = m_to_s(i); + } else { + /* there should be stuff here which calculates balance and + volume on a stereo device. will do it eventually */ + if (driver->ops->get_output_volume) + i = driver->ops->get_output_volume(driver); + if (driver->ops->get_output_balance) + j = driver->ops->get_output_balance(driver); + i = b_to_s(i,j); + } + return COPY_OUT(arg, i); + + default: + /* Play like we support other things */ + return COPY_OUT(arg, i); + } + } +} + static int sparcaudio_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { int retval = 0; struct audio_info ainfo; + if (((cmd >> 8) & 0xff) == 'M') { + return sparcaudio_mixer_ioctl(inode, file, cmd, arg); + } + switch (cmd) { case SNDCTL_DSP_SYNC: case AUDIO_DRAIN: @@ -339,6 +468,40 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, } break; + case AUDIO_FLUSH: + if (driver->output_active && (file->f_mode & FMODE_WRITE)) { + wake_up_interruptible(&driver->output_write_wait); + driver->ops->stop_output(driver); + driver->output_active = 0; + driver->output_front = 0; + driver->output_rear = 0; + driver->output_count = 0; + driver->output_size = 0; + driver->playing_count = 0; + } + if (driver->input_active && (file->f_mode & FMODE_READ)) { + wake_up_interruptible(&driver->input_read_wait); + driver->ops->stop_input(driver); + driver->input_active = 0; + driver->input_front = 0; + driver->input_rear = 0; + driver->input_count = 0; + driver->recording_count = 0; + } + if ((file->f_mode & FMODE_READ) && + !(driver->flags & SDF_OPEN_READ)) { + driver->ops->start_input(driver, + driver->input_buffers[driver->input_front], + 4096); + driver->input_active = 1; + } + if ((file->f_mode & FMODE_WRITE) && + !(driver->flags & SDF_OPEN_WRITE)) { + sparcaudio_sync_output(driver); + } + break; + + case AUDIO_GETDEV: if (driver->ops->sunaudio_getdev) { audio_device_t tmp; @@ -349,18 +512,17 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, } else retval = -EINVAL; - printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV\n"); break; case AUDIO_GETDEV_SUNOS: if (driver->ops->sunaudio_getdev_sunos) { int tmp=driver->ops->sunaudio_getdev_sunos(driver); - copy_to_user_ret((int *)arg, &tmp, sizeof(tmp), -EFAULT); + if (put_user(tmp, (int *)arg)) + retval = -EFAULT; } else retval = -EINVAL; - printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV_SUNOS\n"); break; case AUDIO_GETINFO: @@ -388,7 +550,7 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, if (driver->ops->get_input_ports) ainfo.record.avail_ports = driver->ops->get_input_ports(driver); - ainfo.record.buffer_size = PAGE_SIZE; + ainfo.record.buffer_size = 4096; ainfo.record.samples = 0; ainfo.record.eof = 0; ainfo.record.pause = 0; @@ -422,7 +584,8 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, if (driver->ops->get_output_ports) ainfo.play.avail_ports = driver->ops->get_output_ports(driver); - ainfo.play.buffer_size = PAGE_SIZE; + /* This is not defined in the play context in Solaris */ + ainfo.play.buffer_size = 0; ainfo.play.samples = 0; ainfo.play.eof = 0; ainfo.play.pause = 0; @@ -430,7 +593,7 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, ainfo.play.waiting = waitqueue_active(&driver->open_wait); if (driver->ops->get_output_balance) ainfo.play.balance = - driver->ops->get_output_balance(driver); + (unsigned char)driver->ops->get_output_balance(driver); ainfo.play.minordev = 4; ainfo.play.open = 1; ainfo.play.active = driver->output_active; @@ -441,9 +604,7 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, if (driver->ops->get_output_muted) ainfo.output_muted = - driver->ops->get_output_muted(driver); - - printk("sparcaudio_ioctl: AUDIO_GETINFO\n"); + (unsigned char)driver->ops->get_output_muted(driver); copy_to_user_ret((struct audio_info *)arg, &ainfo, sizeof(ainfo), -EFAULT); @@ -452,7 +613,7 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, case AUDIO_SETINFO: { - audio_info_t curinfo; + audio_info_t curinfo, newinfo; copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT); @@ -531,61 +692,56 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, break; } - curinfo.record.encoding = (Modify(ainfo.record.encoding) ? - ainfo.record.encoding : - driver->ops->get_input_encoding(driver)); - curinfo.record.sample_rate = (Modify(ainfo.record.sample_rate) ? - ainfo.record.sample_rate : - driver->ops->get_input_rate(driver)); - curinfo.record.precision = (Modify(ainfo.record.precision) ? - ainfo.record.precision : - driver->ops->get_input_precision(driver)); - curinfo.record.channels = (Modify(ainfo.record.channels) ? - ainfo.record.channels : - driver->ops->get_input_channels(driver)); - switch (curinfo.record.encoding) { + curinfo.record.encoding = driver->ops->get_input_encoding(driver); + curinfo.record.sample_rate = driver->ops->get_input_rate(driver); + curinfo.record.precision = driver->ops->get_input_precision(driver); + curinfo.record.channels = driver->ops->get_input_channels(driver); + newinfo.record.encoding = Modify(ainfo.record.encoding) ? + ainfo.record.encoding : curinfo.record.encoding; + newinfo.record.sample_rate = Modify(ainfo.record.sample_rate)? + ainfo.record.sample_rate : curinfo.record.sample_rate; + newinfo.record.precision = Modify(ainfo.record.precision) ? + ainfo.record.precision : curinfo.record.precision; + newinfo.record.channels = Modify(ainfo.record.channels) ? + ainfo.record.channels : curinfo.record.channels; + + switch (newinfo.record.encoding) { case AUDIO_ENCODING_ALAW: case AUDIO_ENCODING_ULAW: - if (Modify(ainfo.record.precision) && - ainfo.record.precision != 8) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.record.channels) && - ainfo.record.channels != 1) { - retval = -EINVAL; - break; - } - break; - case AUDIO_ENCODING_LINEAR: - case AUDIO_ENCODING_LINEARLE: - if (Modify(ainfo.record.precision) && - ainfo.record.precision != 16) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.record.channels) && - (ainfo.record.channels != 1 && - ainfo.record.channels != 2)) - { - retval = -EINVAL; - break; - } - break; - case AUDIO_ENCODING_LINEAR8: - if (Modify(ainfo.record.precision) && - ainfo.record.precision != 8) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.record.channels) && - (ainfo.record.channels != 1 && - ainfo.record.channels != 2)) - { - retval = -EINVAL; - break; - } - } + if (newinfo.record.precision != 8) { + retval = -EINVAL; + break; + } + if (newinfo.record.channels != 1) { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR: + case AUDIO_ENCODING_LINEARLE: + if (newinfo.record.precision != 16) { + retval = -EINVAL; + break; + } + if (newinfo.record.channels != 1 && + newinfo.record.channels != 2) + { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR8: + if (newinfo.record.precision != 8) { + retval = -EINVAL; + break; + } + if (newinfo.record.channels != 1 && + newinfo.record.channels != 2) + { + retval = -EINVAL; + break; + } + } if (retval < 0) break; @@ -604,61 +760,56 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, break; } - curinfo.play.encoding = (Modify(ainfo.play.encoding) ? - ainfo.play.encoding : - driver->ops->get_output_encoding(driver)); - curinfo.play.sample_rate = (Modify(ainfo.play.sample_rate) ? - ainfo.play.sample_rate : - driver->ops->get_output_rate(driver)); - curinfo.play.precision = (Modify(ainfo.play.precision) ? - ainfo.play.precision : - driver->ops->get_output_precision(driver)); - curinfo.play.channels = (Modify(ainfo.play.channels) ? - ainfo.play.channels : - driver->ops->get_output_channels(driver)); - switch (curinfo.play.encoding) { + curinfo.play.encoding = driver->ops->get_output_encoding(driver); + curinfo.play.sample_rate = driver->ops->get_output_rate(driver); + curinfo.play.precision = driver->ops->get_output_precision(driver); + curinfo.play.channels = driver->ops->get_output_channels(driver); + newinfo.play.encoding = Modify(ainfo.play.encoding) ? + ainfo.play.encoding : curinfo.play.encoding; + newinfo.play.sample_rate = Modify(ainfo.play.sample_rate) ? + ainfo.play.sample_rate : curinfo.play.sample_rate; + newinfo.play.precision = Modify(ainfo.play.precision) ? + ainfo.play.precision : curinfo.play.precision; + newinfo.play.channels = Modify(ainfo.play.channels) ? + ainfo.play.channels : curinfo.play.channels; + + switch (newinfo.play.encoding) { case AUDIO_ENCODING_ALAW: case AUDIO_ENCODING_ULAW: - if (Modify(ainfo.play.precision) && - ainfo.play.precision != 8) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.play.channels) && - ainfo.play.channels != 1) { - retval = -EINVAL; - break; - } - break; - case AUDIO_ENCODING_LINEAR: - case AUDIO_ENCODING_LINEARLE: - if (Modify(ainfo.play.precision) && - ainfo.play.precision != 16) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.play.channels) && - (ainfo.play.channels != 1 && - ainfo.play.channels != 2)) - { - retval = -EINVAL; - break; - } - break; - case AUDIO_ENCODING_LINEAR8: - if (Modify(ainfo.play.precision) && - ainfo.play.precision != 8) { - retval = -EINVAL; - break; - } - if (Modify(ainfo.play.channels) && - (ainfo.play.channels != 1 && - ainfo.play.channels != 2)) - { - retval = -EINVAL; - break; - } - } + if (newinfo.play.precision != 8) { + retval = -EINVAL; + break; + } + if (newinfo.play.channels != 1) { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR: + case AUDIO_ENCODING_LINEARLE: + if (newinfo.play.precision != 16) { + retval = -EINVAL; + break; + } + if (newinfo.play.channels != 1 && + newinfo.play.channels != 2) + { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR8: + if (newinfo.play.precision != 8) { + retval = -EINVAL; + break; + } + if (newinfo.play.channels != 1 && + newinfo.play.channels != 2) + { + retval = -EINVAL; + break; + } + } if (retval < 0) break; @@ -686,8 +837,20 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, } } + /* Maybe this should be a routine instead of a macro */ +#define IF_SET_DO(x,y) if ((x) && Modify(y)) x(driver, y) +#define IF_SETC_DO(x,y) if ((x) && Modifyc(y)) x(driver, y) + IF_SETC_DO(driver->ops->set_input_balance, (int)ainfo.record.balance); + IF_SETC_DO(driver->ops->set_output_balance, (int)ainfo.play.balance); + IF_SET_DO(driver->ops->set_input_volume, ainfo.record.gain); + IF_SET_DO(driver->ops->set_output_volume, ainfo.play.gain); + IF_SET_DO(driver->ops->set_input_port, ainfo.record.port); + IF_SET_DO(driver->ops->set_output_port, ainfo.play.port); + IF_SET_DO(driver->ops->set_monitor_volume, ainfo.monitor_gain); + IF_SETC_DO(driver->ops->set_output_muted, (int)ainfo.output_muted); +#undef IF_SET_DO +#undef IF_SETC_DO - printk("sparcaudio_ioctl: AUDIO_SETINFO\n"); break; } @@ -696,8 +859,6 @@ static int sparcaudio_ioctl(struct inode * inode, struct file * file, retval = driver->ops->ioctl(inode,file,cmd,arg,driver); else { retval = -EINVAL; - - printk("sparcaudio_ioctl: 0x%x\n", cmd); } } @@ -725,77 +886,83 @@ static struct file_operations sparcaudioctl_fops = { static int sparcaudio_open(struct inode * inode, struct file * file) { - int err; - - /* A low-level audio driver must exist. */ - if (!driver) - return -ENODEV; - - if (MINOR(inode->i_rdev) == 5) { - - file->f_op = &sparcaudioctl_fops; - - MOD_INC_USE_COUNT; - - return 0; - } - - /* We only support minor #4 (/dev/audio) right now. */ - if (MINOR(inode->i_rdev) != 4) - return -ENXIO; - - /* If the driver is busy, then wait to get through. */ - retry_open: + int minor = MINOR(inode->i_rdev); + int err; + + /* A low-level audio driver must exist. */ + if (!driver) + return -ENODEV; + + switch (minor) { + case SPARCAUDIO_AUDIOCTL_MINOR: + file->f_op = &sparcaudioctl_fops; + break; + + case SPARCAUDIO_DSP16_MINOR: + case SPARCAUDIO_DSP_MINOR: + case SPARCAUDIO_AUDIO_MINOR: + /* If the driver is busy, then wait to get through. */ + retry_open: if (file->f_mode & FMODE_READ && driver->flags & SDF_OPEN_READ) { - if (file->f_flags & O_NONBLOCK) - return -EBUSY; - - interruptible_sleep_on(&driver->open_wait); - if (signal_pending(current)) - return -EINTR; - goto retry_open; + if (file->f_flags & O_NONBLOCK) + return -EBUSY; + + interruptible_sleep_on(&driver->open_wait); + if (signal_pending(current)) + return -EINTR; + goto retry_open; } if (file->f_mode & FMODE_WRITE && driver->flags & SDF_OPEN_WRITE) { - if (file->f_flags & O_NONBLOCK) - return -EBUSY; - - interruptible_sleep_on(&driver->open_wait); - if (signal_pending(current)) - return -EINTR; - goto retry_open; + if (file->f_flags & O_NONBLOCK) + return -EBUSY; + + interruptible_sleep_on(&driver->open_wait); + if (signal_pending(current)) + return -EINTR; + goto retry_open; } + /* Allow the low-level driver to initialize itself. */ + if (driver->ops->open) { + err = driver->ops->open(inode,file,driver); + if (err < 0) + return err; + } + /* Mark the driver as locked for read and/or write. */ if (file->f_mode & FMODE_READ) { - driver->input_offset = 0; - driver->input_front = 0; - driver->input_rear = 0; - driver->input_count = 0; - driver->ops->start_input(driver, driver->input_buffers[driver->input_front], - PAGE_SIZE); - driver->input_active = 1; - driver->flags |= SDF_OPEN_READ; + driver->input_offset = 0; + driver->input_front = 0; + driver->input_rear = 0; + driver->input_count = 0; + driver->recording_count = 0; + driver->ops->start_input(driver, driver->input_buffers[driver->input_front], + 4096); + driver->input_active = 1; + driver->flags |= SDF_OPEN_READ; } if (file->f_mode & FMODE_WRITE) { - driver->output_size = 0; - driver->output_front = 0; - driver->output_rear = 0; - driver->output_count = 0; - driver->output_active = 0; - driver->flags |= SDF_OPEN_WRITE; + driver->playing_count = 0; + driver->output_size = 0; + driver->output_front = 0; + driver->output_rear = 0; + driver->output_count = 0; + driver->output_active = 0; + driver->flags |= SDF_OPEN_WRITE; } - - /* Allow the low-level driver to initialize itself. */ - if (driver->ops->open) { - err = driver->ops->open(inode,file,driver); - if (err < 0) - return err; - } - - MOD_INC_USE_COUNT; - - /* Success! */ - return 0; + break; + case SPARCAUDIO_MIXER_MINOR: + file->f_op = &sparcaudioctl_fops; + break; + + default: + return -ENXIO; + } + + MOD_INC_USE_COUNT; + + /* Success! */ + return 0; } static int sparcaudio_release(struct inode * inode, struct file * file) @@ -876,3 +1043,22 @@ void cleanup_module(void) unregister_chrdev(SOUND_MAJOR, "sparcaudio"); } #endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/drivers/sbus/audio/audio.h b/drivers/sbus/audio/audio.h deleted file mode 100644 index 5884aeaab..000000000 --- a/drivers/sbus/audio/audio.h +++ /dev/null @@ -1,384 +0,0 @@ -/* - * drivers/sbus/audio/audio.h - * - * Sparc Audio Midlayer - * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) - */ - -#ifndef _AUDIO_H_ -#define _AUDIO_H_ - -/* - * SunOS/Solaris /dev/audio interface - */ - -#include <linux/types.h> -#include <linux/time.h> -#include <linux/ioctl.h> - -/* - * This structure contains state information for audio device IO streams. - */ -typedef struct audio_prinfo { - /* - * The following values describe the audio data encoding. - */ - unsigned int sample_rate; /* samples per second */ - unsigned int channels; /* number of interleaved channels */ - unsigned int precision; /* bit-width of each sample */ - unsigned int encoding; /* data encoding method */ - - /* - * The following values control audio device configuration - */ - unsigned int gain; /* gain level: 0 - 255 */ - unsigned int port; /* selected I/O port (see below) */ - unsigned int avail_ports; /* available I/O ports (see below) */ - unsigned int _xxx[2]; /* Reserved for future use */ - - unsigned int buffer_size; /* I/O buffer size */ - - /* - * The following values describe driver state - */ - unsigned int samples; /* number of samples converted */ - unsigned int eof; /* End Of File counter (play only) */ - - unsigned char pause; /* non-zero for pause, zero to resume */ - unsigned char error; /* non-zero if overflow/underflow */ - unsigned char waiting; /* non-zero if a process wants access */ - unsigned char balance; /* stereo channel balance */ - - unsigned short minordev; - - /* - * The following values are read-only state flags - */ - unsigned char open; /* non-zero if open access permitted */ - unsigned char active; /* non-zero if I/O is active */ -} audio_prinfo_t; - - -/* - * This structure describes the current state of the audio device. - */ -typedef struct audio_info { - /* - * Per-stream information - */ - audio_prinfo_t play; /* output status information */ - audio_prinfo_t record; /* input status information */ - - /* - * Per-unit/channel information - */ - unsigned int monitor_gain; /* input to output mix: 0 - 255 */ - unsigned char output_muted; /* non-zero if output is muted */ - unsigned char _xxx[3]; /* Reserved for future use */ - unsigned int _yyy[3]; /* Reserved for future use */ -} audio_info_t; - - -/* - * Audio encoding types - */ -#define AUDIO_ENCODING_NONE (0) /* no encoding assigned */ -#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */ -#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */ -#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */ -#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */ -#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */ -#define AUDIO_ENCODING_LINEARLE (106) /* Linear PCM LE encoding */ - -/* - * These ranges apply to record, play, and monitor gain values - */ -#define AUDIO_MIN_GAIN (0) /* minimum gain value */ -#define AUDIO_MAX_GAIN (255) /* maximum gain value */ - -/* - * These values apply to the balance field to adjust channel gain values - */ -#define AUDIO_LEFT_BALANCE (0) /* left channel only */ -#define AUDIO_MID_BALANCE (32) /* equal left/right channel */ -#define AUDIO_RIGHT_BALANCE (64) /* right channel only */ -#define AUDIO_BALANCE_SHIFT (3) - -/* - * Generic minimum/maximum limits for number of channels, both modes - */ -#define AUDIO_MIN_PLAY_CHANNELS (1) -#define AUDIO_MAX_PLAY_CHANNELS (4) -#define AUDIO_MIN_REC_CHANNELS (1) -#define AUDIO_MAX_REC_CHANNELS (4) - -/* - * Generic minimum/maximum limits for sample precision - */ -#define AUDIO_MIN_PLAY_PRECISION (8) -#define AUDIO_MAX_PLAY_PRECISION (32) -#define AUDIO_MIN_REC_PRECISION (8) -#define AUDIO_MAX_REC_PRECISION (32) - -/* - * Define some convenient names for typical audio ports - */ -/* - * output ports (several may be enabled simultaneously) - */ -#define AUDIO_SPEAKER 0x01 /* output to built-in speaker */ -#define AUDIO_HEADPHONE 0x02 /* output to headphone jack */ -#define AUDIO_LINE_OUT 0x04 /* output to line out */ - -/* - * input ports (usually only one at a time) - */ -#define AUDIO_MICROPHONE 0x01 /* input from microphone */ -#define AUDIO_LINE_IN 0x02 /* input from line in */ -#define AUDIO_CD 0x04 /* input from on-board CD inputs */ -#define AUDIO_INTERNAL_CD_IN AUDIO_CD /* input from internal CDROM */ -/* Supposedly an undocumented feature of the 4231 */ -#define AUDIO_ANALOG_LOOPBACK 0x40 - - -/* - * This macro initializes an audio_info structure to 'harmless' values. - * Note that (~0) might not be a harmless value for a flag that was - * a signed int. - */ -#define AUDIO_INITINFO(i) { \ - unsigned int *__x__; \ - for (__x__ = (unsigned int *)(i); \ - (char *) __x__ < (((char *)(i)) + sizeof (audio_info_t)); \ - *__x__++ = ~0); \ -} - - -/* - * These allow testing for what the user wants to set - */ -#define AUD_INITVALUE (~0) -#define Modify(X) ((unsigned int)(X) != AUD_INITVALUE) -#define Modifys(X) ((X) != (unsigned short)AUD_INITVALUE) -#define Modifyc(X) ((X) != (unsigned char)AUD_INITVALUE) - -/* - * Parameter for the AUDIO_GETDEV ioctl to determine current - * audio devices. - */ -#define MAX_AUDIO_DEV_LEN (16) -typedef struct audio_device { - char name[MAX_AUDIO_DEV_LEN]; - char version[MAX_AUDIO_DEV_LEN]; - char config[MAX_AUDIO_DEV_LEN]; -} audio_device_t; - - -/* - * Ioctl calls for the audio device. - */ - -/* - * AUDIO_GETINFO retrieves the current state of the audio device. - * - * AUDIO_SETINFO copies all fields of the audio_info structure whose - * values are not set to the initialized value (-1) to the device state. - * It performs an implicit AUDIO_GETINFO to return the new state of the - * device. Note that the record.samples and play.samples fields are set - * to the last value before the AUDIO_SETINFO took effect. This allows - * an application to reset the counters while atomically retrieving the - * last value. - * - * AUDIO_DRAIN suspends the calling process until the write buffers are - * empty. - * - * AUDIO_GETDEV returns a structure of type audio_device_t which contains - * three strings. The string "name" is a short identifying string (for - * example, the SBus Fcode name string), the string "version" identifies - * the current version of the device, and the "config" string identifies - * the specific configuration of the audio stream. All fields are - * device-dependent -- see the device specific manual pages for details. - */ -#define AUDIO_GETINFO _IOR('A', 1, audio_info_t) -#define AUDIO_SETINFO _IOWR('A', 2, audio_info_t) -#define AUDIO_DRAIN _IO('A', 3) -#define AUDIO_GETDEV _IOR('A', 4, audio_device_t) -#define AUDIO_GETDEV_SUNOS _IOR('A', 4, int) - -/* Define possible audio hardware configurations for - * old SunOS-style AUDIO_GETDEV ioctl */ - -#define AUDIO_DEV_UNKNOWN (0) /* not defined */ -#define AUDIO_DEV_AMD (1) /* audioamd device */ -#define AUDIO_DEV_SPEAKERBOX (2) /* dbri device with speakerbox */ -#define AUDIO_DEV_CODEC (3) /* dbri device (internal speaker) */ -#define AUDIO_DEV_CS4231 (5) /* cs4231 device */ - -/* - * The following ioctl sets the audio device into an internal loopback mode, - * if the hardware supports this. The argument is TRUE to set loopback, - * FALSE to reset to normal operation. If the hardware does not support - * internal loopback, the ioctl should fail with EINVAL. - */ -#define AUDIO_DIAG_LOOPBACK _IOW('A', 101, int) - -#ifdef notneeded -/* - * Structure sent up as a M_PROTO message on trace streams - */ -typedef struct audtrace_hdr audtrace_hdr_t; -struct audtrace_hdr { - unsigned int seq; /* Sequence number (per-aud_stream) */ - int type; /* device-dependent */ - struct timeval timestamp; - char _f[8]; /* filler */ -}; -#endif - -/* - * Linux kernel internal implementation. - */ - -#ifdef __KERNEL__ - -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/tqueue.h> -#include <linux/wait.h> - -#define SDF_OPEN_WRITE 0x00000001 -#define SDF_OPEN_READ 0x00000002 - -struct sparcaudio_driver -{ - const char * name; - struct sparcaudio_operations *ops; - void *private; - unsigned long flags; - - /* Processes blocked on open() sit here. */ - struct wait_queue *open_wait; - - /* Task queue for this driver's bottom half. */ - struct tq_struct tqueue; - - /* Support for a circular queue of output buffers. */ - __u8 **output_buffers; - size_t *output_sizes, output_size; - int num_output_buffers, output_front, output_rear; - int output_count, output_active; - struct wait_queue *output_write_wait, *output_drain_wait; - - /* Support for a circular queue of input buffers. */ - __u8 **input_buffers; - int input_offset; - int num_input_buffers, input_front, input_rear; - int input_count, input_active; - struct wait_queue *input_read_wait; -}; - -struct sparcaudio_operations -{ - int (*open)(struct inode *, struct file *, struct sparcaudio_driver *); - void (*release)(struct inode *, struct file *, struct sparcaudio_driver *); - int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long, - struct sparcaudio_driver *); - - /* Ask driver to begin playing a buffer. */ - void (*start_output)(struct sparcaudio_driver *, __u8 *, unsigned long); - - /* Ask driver to stop playing a buffer. */ - void (*stop_output)(struct sparcaudio_driver *); - - /* Ask driver to begin recording into a buffer. */ - void (*start_input)(struct sparcaudio_driver *, __u8 *, unsigned long); - - /* Ask driver to stop recording. */ - void (*stop_input)(struct sparcaudio_driver *); - - /* Return driver name/version to caller. (/dev/audio specific) */ - void (*sunaudio_getdev)(struct sparcaudio_driver *, audio_device_t *); - - /* Get and set the output volume. (0-255) */ - int (*set_output_volume)(struct sparcaudio_driver *, int); - int (*get_output_volume)(struct sparcaudio_driver *); - - /* Get and set the input volume. (0-255) */ - int (*set_input_volume)(struct sparcaudio_driver *, int); - int (*get_input_volume)(struct sparcaudio_driver *); - - /* Get and set the monitor volume. (0-255) */ - int (*set_monitor_volume)(struct sparcaudio_driver *, int); - int (*get_monitor_volume)(struct sparcaudio_driver *); - - /* Get and set the output balance. (0-64) */ - int (*set_output_balance)(struct sparcaudio_driver *, int); - int (*get_output_balance)(struct sparcaudio_driver *); - - /* Get and set the input balance. (0-64) */ - int (*set_input_balance)(struct sparcaudio_driver *, int); - int (*get_input_balance)(struct sparcaudio_driver *); - - /* Get and set the output channels. (1-4) */ - int (*set_output_channels)(struct sparcaudio_driver *, int); - int (*get_output_channels)(struct sparcaudio_driver *); - - /* Get and set the input channels. (1-4) */ - int (*set_input_channels)(struct sparcaudio_driver *, int); - int (*get_input_channels)(struct sparcaudio_driver *); - - /* Get and set the output precision. (8-32) */ - int (*set_output_precision)(struct sparcaudio_driver *, int); - int (*get_output_precision)(struct sparcaudio_driver *); - - /* Get and set the input precision. (8-32) */ - int (*set_input_precision)(struct sparcaudio_driver *, int); - int (*get_input_precision)(struct sparcaudio_driver *); - - /* Get and set the output port. () */ - int (*set_output_port)(struct sparcaudio_driver *, int); - int (*get_output_port)(struct sparcaudio_driver *); - - /* Get and set the input port. () */ - int (*set_input_port)(struct sparcaudio_driver *, int); - int (*get_input_port)(struct sparcaudio_driver *); - - /* Get and set the output encoding. () */ - int (*set_output_encoding)(struct sparcaudio_driver *, int); - int (*get_output_encoding)(struct sparcaudio_driver *); - - /* Get and set the input encoding. () */ - int (*set_input_encoding)(struct sparcaudio_driver *, int); - int (*get_input_encoding)(struct sparcaudio_driver *); - - /* Get and set the output rate. () */ - int (*set_output_rate)(struct sparcaudio_driver *, int); - int (*get_output_rate)(struct sparcaudio_driver *); - - /* Get and set the input rate. () */ - int (*set_input_rate)(struct sparcaudio_driver *, int); - int (*get_input_rate)(struct sparcaudio_driver *); - - /* Return driver number to caller. (SunOS /dev/audio specific) */ - int (*sunaudio_getdev_sunos)(struct sparcaudio_driver *); - - /* Get available ports */ - int (*get_output_ports)(struct sparcaudio_driver *); - int (*get_input_ports)(struct sparcaudio_driver *); - - /* Get and set output mute */ - int (*set_output_muted)(struct sparcaudio_driver *, int); - int (*get_output_muted)(struct sparcaudio_driver *); -}; - -extern int register_sparcaudio_driver(struct sparcaudio_driver *); -extern int unregister_sparcaudio_driver(struct sparcaudio_driver *); -extern void sparcaudio_output_done(struct sparcaudio_driver *); -extern void sparcaudio_input_done(struct sparcaudio_driver *); -extern int sparcaudio_init(void); -extern int amd7930_init(void); -extern int cs4231_init(void); - -#endif /* __KERNEL__ */ - -#endif /* _AUDIO_H */ diff --git a/drivers/sbus/audio/cs4215.h b/drivers/sbus/audio/cs4215.h index 966339a02..ec8de8fad 100644 --- a/drivers/sbus/audio/cs4215.h +++ b/drivers/sbus/audio/cs4215.h @@ -61,7 +61,7 @@ static struct { }; #define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */ -#define CS4215_12_MASK 0xfcbf /* Mask off reseved bits in slot 1 & 2 */ +#define CS4215_12_MASK 0xfcbf /* Mask off reserved bits in slot 1 & 2 */ /* Time Slot 3, Serial Port Control register */ #define CS4215_XEN (1<<0) /* 0: Enable serial output */ @@ -109,7 +109,7 @@ static struct { /* Time Slot 7, Input Setting */ #define CS4215_LG(v) v /* Left Gain Setting 0xf: 22.5 dB */ #define CS4215_IS (1<<4) /* Input Select: 1=Microphone, 0=Line */ -#define CS4215_OVR (1<<5) /* 1: Overrange condition occured */ +#define CS4215_OVR (1<<5) /* 1: Overrange condition occurred */ #define CS4215_PIO0 (1<<6) /* Parallel I/O 0 */ #define CS4215_PIO1 (1<<7) diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c index 346f07185..24a460973 100644 --- a/drivers/sbus/audio/cs4231.c +++ b/drivers/sbus/audio/cs4231.c @@ -1,11 +1,17 @@ /* * drivers/sbus/audio/cs4231.c * + * Copyright (C) 1996, 1997 Derrick J Brashear (shadow@andrew.cmu.edu) + * + * Based on the AMD7930 driver: * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) - * Copyright (C) 1996 Derrick J Brashear (shadow@andrew.cmu.edu) * * This is the lowlevel driver for the CS4231 audio chip found on some - * sun4m machines. + * sun4m and sun4u machines. + * + * This was culled from the Crystal docs on the 4231a, and the addendum they + * faxed me on the 4231. + * The APC DMA controller support unfortunately is not documented. Thanks, Sun */ #include <linux/module.h> @@ -15,405 +21,957 @@ #include <linux/interrupt.h> #include <linux/malloc.h> #include <linux/init.h> +#include <linux/delay.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/delay.h> +#include <asm/pgtable.h> #include <asm/sbus.h> -#include "audio.h" +#include <asm/audioio.h> #include "cs4231.h" -/* Stolen for now from compat.h */ -#ifndef MAX /* Usually found in <sys/param.h>. */ -#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#undef __CS4231_DEBUG +#undef __CS4231_TRACE +#undef __CS4231_ERROR +#ifdef __CS4231_ERROR +#define eprintk(x) printk x +#else +#define eprintk(x) #endif -#ifndef MIN /* Usually found in <sys/param.h>. */ -#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#ifdef __CS4231_TRACE +#define tprintk(x) printk x +#else +#define tprintk(x) +#endif +#ifdef __CS4231_DEBUG +#define dprintk(x) printk x +#else +#define dprintk(x) #endif #define MAX_DRIVERS 1 static struct sparcaudio_driver drivers[MAX_DRIVERS]; static int num_drivers; -static int cs4231_playintr(struct sparcaudio_driver *drv); +static int cs4231_record_gain(struct sparcaudio_driver *drv, int value, + unsigned char balance); +static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, + unsigned char balance); +static void cs4231_ready(struct sparcaudio_driver *drv); +static void cs4231_playintr(struct sparcaudio_driver *drv); static int cs4231_recintr(struct sparcaudio_driver *drv); -static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value); -static void cs4231_mute(struct sparcaudio_driver *drv); +static int cs4231_output_muted(struct sparcaudio_driver *drv, int value); static void cs4231_pollinput(struct sparcaudio_driver *drv); +static int cs4231_length_to_samplecount(struct audio_prinfo *thisdir, + unsigned int length); +static void cs4231_getsamplecount(struct sparcaudio_driver *drv, + unsigned int length, unsigned int value); -#define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000); +#define CHIP_READY udelay(100); cs4231_ready(drv); mdelay(1); -/* Disable mode change, let chip auto-calibrate */ -static void cs4231_ready(struct sparcaudio_driver *drv) +/* Enable cs4231 interrupts atomically. */ +static __inline__ void cs4231_enable_interrupts(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - unsigned int x = 0; + register unsigned long flags; - cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END; - while (cs4231_chip->pioregs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) { - x++; + if (cs4231_chip->status & CS_STATUS_INTS_ON) + return; + + tprintk(("enabling interrupts\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0xa; + cs4231_chip->regs->idr = INTR_ON; + restore_flags(flags); + + cs4231_chip->status |= CS_STATUS_INTS_ON; +} + +/* Disable cs4231 interrupts atomically. */ +static __inline__ void cs4231_disable_interrupts(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + register unsigned long flags; + + if (!(cs4231_chip->status & CS_STATUS_INTS_ON)) + return; + + tprintk(("disabling interrupts\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0xa; + cs4231_chip->regs->idr = INTR_OFF; + restore_flags(flags); + + cs4231_chip->status &= ~CS_STATUS_INTS_ON; +} + +static __inline__ void cs4231_enable_play(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + register unsigned long flags; + + tprintk(("enabling play\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0x9; + cs4231_chip->regs->idr |= PEN_ENABLE; + restore_flags(flags); +} + +static __inline__ void cs4231_disable_play(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + register unsigned long flags; + + tprintk(("disabling play\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0x9; + cs4231_chip->regs->idr &= PEN_DISABLE; + restore_flags(flags); +} + +static __inline__ void cs4231_enable_rec(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + register unsigned long flags; + + tprintk(("enabling rec\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0x9; + cs4231_chip->regs->idr |= CEN_ENABLE; + restore_flags(flags); +} + +static __inline__ void cs4231_disable_rec(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + register unsigned long flags; + + tprintk(("disabling rec\n")); + save_flags(flags); + cli(); + cs4231_chip->regs->iar = 0x9; + cs4231_chip->regs->idr &= CEN_DISABLE; + restore_flags(flags); +} + +static int +cs4231_rate_to_bits(struct sparcaudio_driver *drv, int value) +{ + int set_bits; + + switch (value) { + case 5512: + set_bits = CS4231_DFR_5512; + break; + case 6615: + set_bits = CS4231_DFR_6615; + break; + case 8000: + set_bits = CS4231_DFR_8000; + break; + case 9600: + set_bits = CS4231_DFR_9600; + break; + case 11025: + set_bits = CS4231_DFR_11025; + break; + case 16000: + set_bits = CS4231_DFR_16000; + break; + case 18900: + set_bits = CS4231_DFR_18900; + break; + case 22050: + set_bits = CS4231_DFR_22050; + break; + case 27429: + set_bits = CS4231_DFR_27429; + break; + case 32000: + set_bits = CS4231_DFR_32000; + break; + case 33075: + set_bits = CS4231_DFR_33075; + break; + case 37800: + set_bits = CS4231_DFR_37800; + break; + case 44100: + set_bits = CS4231_DFR_44100; + break; + case 48000: + set_bits = CS4231_DFR_48000; + break; + default: + set_bits = -(EINVAL); + break; } + return set_bits; +} - x = 0; - cs4231_chip->pioregs->iar = 0x0b; - while (cs4231_chip->pioregs->idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) { - x++; +static int +cs4231_encoding_to_bits(struct sparcaudio_driver *drv, int value) +{ + int set_bits; + + switch (value) { + case AUDIO_ENCODING_ULAW: + set_bits = CS4231_DFR_ULAW; + break; + case AUDIO_ENCODING_ALAW: + set_bits = CS4231_DFR_ALAW; + break; + case AUDIO_ENCODING_DVI: + set_bits = CS4231_DFR_ADPCM; + break; + case AUDIO_ENCODING_LINEARLE: + set_bits = CS4231_DFR_LINEARLE; + break; + case AUDIO_ENCODING_LINEAR: + set_bits = CS4231_DFR_LINEARBE; + break; + case AUDIO_ENCODING_LINEAR8: + set_bits = CS4231_DFR_LINEAR8; + break; + default: + set_bits = -(EINVAL); + break; } + + return set_bits; } -/* Audio interrupt handler. */ -static void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static int +cs4231_set_output_encoding(struct sparcaudio_driver *drv, int value) { - struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - __u8 dummy; - int ic = 1; - - /* Clear the interrupt. */ - dummy = cs4231_chip->dmaregs.dmacsr; - cs4231_chip->dmaregs.dmacsr = dummy; + int tmp_bits, set_bits; + + tprintk(("output encoding %d\n", value)); + if (value != 0) { + set_bits = cs4231_encoding_to_bits(drv, value); + if (set_bits >= 0) { + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); + + CHIP_READY - /* now go through and figure out what gets to claim the interrupt */ - if (dummy & CS_PLAY_INT) { - if (dummy & CS_XINT_PNVA) { - /* recalculate number of samples */ - cs4231_playintr(drv); + cs4231_chip->perchip_info.play.encoding = value; + return 0; } - ic = 0; } - if (dummy & CS_CAPT_INT) { - if (dummy & CS_XINT_CNVA) { - /* recalculate number of samples */ - cs4231_recintr(drv); + eprintk(("output enc failed\n")); + return -EINVAL; +} + +static int cs4231_get_output_encoding(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.play.encoding; +} + +static int +cs4231_set_input_encoding(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int tmp_bits, set_bits; + + dprintk(("input encoding %d\n", value)); + if (value != 0) { + set_bits = cs4231_encoding_to_bits(drv, value); + if (set_bits >= 0) { + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_ENCODING(tmp_bits, set_bits); + + CHIP_READY + + cs4231_chip->perchip_info.record.encoding = value; + return 0; } - ic = 0; } - if ((dummy & CS_XINT_CEMP) - && (cs4231_chip->perchip_info.record.active == 0)) - { - ic = 0; + eprintk(("input enc failed\n")); + return -EINVAL; +} + +static int cs4231_get_input_encoding(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.record.encoding; +} + +static int +cs4231_set_output_rate(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int tmp_bits, set_bits; + + tprintk(("output rate %d\n", value)); + if (value != 0) { + set_bits = cs4231_rate_to_bits(drv, value); + if (set_bits >= 0) { + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); + + CHIP_READY + + cs4231_chip->perchip_info.play.sample_rate = value; + return 0; } - if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) { - cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE); - cs4231_chip->pioregs->iar = 0x9; - cs4231_chip->pioregs->idr &= PEN_DISABLE; - - cs4231_mute(drv); + } + eprintk(("output rate failed\n")); + return -EINVAL; +} + +static int cs4231_get_output_rate(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.play.sample_rate; +} + +static int +cs4231_set_input_rate(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int tmp_bits, set_bits; + + dprintk(("input rate %d\n", value)); + if (value != 0) { + set_bits = cs4231_rate_to_bits(drv, value); + if (set_bits >= 0) { + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; + tmp_bits = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = CHANGE_DFR(tmp_bits, set_bits); + + CHIP_READY + + cs4231_chip->perchip_info.record.sample_rate = value; + return 0; + } + } + eprintk(("input rate failed\n")); + return -EINVAL; +} + +static int cs4231_get_input_rate(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.record.sample_rate; +} + +/* Generically we support 4 channels. This hardware does 2 */ +static int +cs4231_set_input_channels(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int tmp_bits; + + dprintk(("input channels %d\n", value)); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c; + tmp_bits = cs4231_chip->regs->idr; + switch (value) { + case 1: + cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); + break; + case 2: + cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); + break; + default: + eprintk(("input chan failed\n")); + return -(EINVAL); + } + + CHIP_READY + + cs4231_chip->perchip_info.record.channels = value; + return 0; +} + +static int cs4231_get_input_channels(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.record.channels; +} + +/* Generically we support 4 channels. This hardware does 2 */ +static int +cs4231_set_output_channels(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int tmp_bits; + + tprintk(("output channels %d\n", value)); + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x8; + tmp_bits = cs4231_chip->regs->idr; + switch (value) { + case 1: + cs4231_chip->regs->idr = CS4231_MONO_ON(tmp_bits); + break; + case 2: + cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits); + break; + default: + eprintk(("output chan failed\n")); + return -(EINVAL); + } + + CHIP_READY - /* recalculate number of samples */ - /* cleanup DMA */ - ic = 0; + cs4231_chip->perchip_info.play.channels = value; + return 0; +} + +static int cs4231_get_output_channels(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.play.channels; +} + +static int cs4231_set_input_precision(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + cs4231_chip->perchip_info.record.precision = value; + return 0; +} + +static int cs4231_get_input_precision(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.record.precision; +} + +static int cs4231_set_output_precision(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + cs4231_chip->perchip_info.play.precision = value; + return 0; +} + +static int cs4231_get_output_precision(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.play.precision; +} + +/* Wait until the auto calibration process has finished */ +static void +cs4231_ready(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + unsigned int x = 0; + + cs4231_chip->regs->iar = IAR_AUTOCAL_END; + while (cs4231_chip->regs->iar == IAR_NOT_READY && x <= CS_TIMEOUT) { + x++; } - if (dummy & CS_GENL_INT) { - ic = 0; + + x = 0; + cs4231_chip->regs->iar = 0x0b; + while (cs4231_chip->regs->idr == AUTOCAL_IN_PROGRESS && x <= CS_TIMEOUT) { + x++; } } /* Set output mute */ -static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value) +static int cs4231_output_muted(struct sparcaudio_driver *drv, int value) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + tprintk(("in cs4231_output_muted: %d\n", value)); if (!value) { - cs4231_chip->pioregs->iar = 0x7; - cs4231_chip->pioregs->idr &= OUTCR_UNMUTE; - cs4231_chip->pioregs->iar = 0x6; - cs4231_chip->pioregs->idr &= OUTCR_UNMUTE; + cs4231_chip->regs->iar = 0x7; + cs4231_chip->regs->idr &= OUTCR_UNMUTE; + cs4231_chip->regs->iar = 0x6; + cs4231_chip->regs->idr &= OUTCR_UNMUTE; cs4231_chip->perchip_info.output_muted = 0; } else { - cs4231_chip->pioregs->iar = 0x7; - cs4231_chip->pioregs->idr |= OUTCR_MUTE; - cs4231_chip->pioregs->iar = 0x6; - cs4231_chip->pioregs->idr |= OUTCR_MUTE; + cs4231_chip->regs->iar = 0x7; + cs4231_chip->regs->idr |= OUTCR_MUTE; + cs4231_chip->regs->iar = 0x6; + cs4231_chip->regs->idr |= OUTCR_MUTE; cs4231_chip->perchip_info.output_muted = 1; } - return /*(cs4231_chip->perchip_info.output_muted)*/; + return 0; } -/* Set chip "output" port */ -static unsigned int cs4231_out_port(struct sparcaudio_driver *drv, unsigned int value) +static int cs4231_get_output_muted(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.output_muted; +} + +static int cs4231_get_output_ports(struct sparcaudio_driver *drv) +{ + return (AUDIO_LINE_OUT | AUDIO_SPEAKER | AUDIO_HEADPHONE); +} + +static int cs4231_get_input_ports(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - unsigned int r = 0; - /* You can have any combo you want. Just don't tell anyone. */ + /* This apparently applies only to APC ultras, not ebus ultras */ + if (cs4231_chip->status & CS_STATUS_IS_ULTRA) + return (AUDIO_LINE_IN | AUDIO_MICROPHONE | AUDIO_ANALOG_LOOPBACK); + else + return (AUDIO_INTERNAL_CD_IN | AUDIO_LINE_IN | AUDIO_MICROPHONE | + AUDIO_ANALOG_LOOPBACK); +} - cs4231_chip->pioregs->iar = 0x1a; - cs4231_chip->pioregs->idr |= MONO_IOCR_MUTE; - cs4231_chip->pioregs->iar = 0x0a; - cs4231_chip->pioregs->idr |= PINCR_LINE_MUTE; - cs4231_chip->pioregs->idr |= PINCR_HDPH_MUTE; +/* Set chip "output" port */ +static int cs4231_set_output_port(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int retval = 0; + + tprintk(("output port: %d\n", value)); + /* Aaaaaah! It's all coming so fast! Turn it all off, then selectively + * enable things. + */ + cs4231_chip->regs->iar = 0x1a; + cs4231_chip->regs->idr |= MONO_IOCR_MUTE; + cs4231_chip->regs->iar = 0x0a; + cs4231_chip->regs->idr |= PINCR_LINE_MUTE; + cs4231_chip->regs->idr |= PINCR_HDPH_MUTE; if (value & AUDIO_SPEAKER) { - cs4231_chip->pioregs->iar = 0x1a; - cs4231_chip->pioregs->idr &= ~MONO_IOCR_MUTE; - r |= AUDIO_SPEAKER; + cs4231_chip->regs->iar = 0x1a; + cs4231_chip->regs->idr &= ~MONO_IOCR_MUTE; + retval |= AUDIO_SPEAKER; } if (value & AUDIO_HEADPHONE) { - cs4231_chip->pioregs->iar = 0x0a; - cs4231_chip->pioregs->idr &= ~PINCR_HDPH_MUTE; - r |= AUDIO_HEADPHONE; + cs4231_chip->regs->iar = 0x0a; + cs4231_chip->regs->idr &= ~PINCR_HDPH_MUTE; + retval |= AUDIO_HEADPHONE; } if (value & AUDIO_LINE_OUT) { - cs4231_chip->pioregs->iar = 0x0a; - cs4231_chip->pioregs->idr &= ~PINCR_LINE_MUTE; - r |= AUDIO_LINE_OUT; + cs4231_chip->regs->iar = 0x0a; + cs4231_chip->regs->idr &= ~PINCR_LINE_MUTE; + retval |= AUDIO_LINE_OUT; } - return (r); + cs4231_chip->perchip_info.play.port = retval; + + return (retval); +} + +static int cs4231_get_output_port(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.play.port; } /* Set chip "input" port */ -static unsigned int cs4231_in_port(struct sparcaudio_driver *drv, unsigned int value) +static int cs4231_set_input_port(struct sparcaudio_driver *drv, int value) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - unsigned int r = 0; - - /* The order of these seems to matter. Can't tell yet why. */ - if (value & AUDIO_INTERNAL_CD_IN) { - cs4231_chip->pioregs->iar = 0x1; - cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr); - cs4231_chip->pioregs->iar = 0x0; - cs4231_chip->pioregs->idr = CDROM_ENABLE(cs4231_chip->pioregs->idr); - r = AUDIO_INTERNAL_CD_IN; + int retval = 0; + + dprintk(("input port: %d\n", value)); + + /* You can have one and only one. This is probably wrong, but + * appears to be how SunOS is doing it. Should be able to mix. + * More work to be done. + */ + + /* Ultra systems do not support AUDIO_INTERNAL_CD_IN */ + /* This apparently applies only to APC ultras, not ebus ultras */ + if (!cs4231_chip->status & CS_STATUS_IS_ULTRA) { + if (value & AUDIO_INTERNAL_CD_IN) { + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = CDROM_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_INTERNAL_CD_IN; + } } if ((value & AUDIO_LINE_IN)) { - cs4231_chip->pioregs->iar = 0x1; - cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr); - cs4231_chip->pioregs->iar = 0x0; - cs4231_chip->pioregs->idr = LINE_ENABLE(cs4231_chip->pioregs->idr); - r = AUDIO_LINE_IN; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = LINE_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_LINE_IN; } else if (value & AUDIO_MICROPHONE) { - cs4231_chip->pioregs->iar = 0x1; - cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr); - cs4231_chip->pioregs->iar = 0x0; - cs4231_chip->pioregs->idr = MIC_ENABLE(cs4231_chip->pioregs->idr); - r = AUDIO_MICROPHONE; + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = MIC_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_MICROPHONE; + } else if (value & AUDIO_ANALOG_LOOPBACK) { + cs4231_chip->regs->iar = 0x1; + cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); + cs4231_chip->regs->iar = 0x0; + cs4231_chip->regs->idr = OUTPUTLOOP_ENABLE(cs4231_chip->regs->idr); + retval = AUDIO_ANALOG_LOOPBACK; } - return (r); + cs4231_chip->perchip_info.record.port = retval; + + return (retval); +} + +static int cs4231_get_input_port(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + return cs4231_chip->perchip_info.record.port; } /* Set chip "monitor" gain */ -static unsigned int cs4231_monitor_gain(struct sparcaudio_driver *drv, unsigned int value) +static int cs4231_set_monitor_volume(struct sparcaudio_driver *drv, int value) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; int a = 0; - a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1)); + tprintk(("monitor gain: %d\n", value)); + + /* This interpolation really sucks. The question is, be compatible + * with ScumOS/Sloaris or not? + */ + a = CS4231_MON_MAX_ATEN - (value * (CS4231_MON_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1)); - cs4231_chip->pioregs->iar = 0x0d; + cs4231_chip->regs->iar = 0x0d; if (a >= CS4231_MON_MAX_ATEN) - cs4231_chip->pioregs->idr = LOOPB_OFF; + cs4231_chip->regs->idr = LOOPB_OFF; else - cs4231_chip->pioregs->idr = ((a << 2) | LOOPB_ON); + cs4231_chip->regs->idr = ((a << 2) | LOOPB_ON); + + if (value == AUDIO_MAX_GAIN) + cs4231_chip->perchip_info.monitor_gain = AUDIO_MAX_GAIN; + else + cs4231_chip->perchip_info.monitor_gain = ((CS4231_MAX_DEV_ATEN - a) * + (AUDIO_MAX_GAIN + 1) / + (CS4231_MAX_DEV_ATEN + 1)); + + return 0; +} + +static int cs4231_get_monitor_volume(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - if (value == AUDIO_MAX_GAIN) return AUDIO_MAX_GAIN; + return (int)cs4231_chip->perchip_info.monitor_gain; +} + +/* But for play/record we have these cheesy jacket routines because of + * how this crap gets set */ +static int cs4231_set_input_volume(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - return ((CS4231_MAX_DEV_ATEN - a) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_DEV_ATEN + 1)); + cs4231_record_gain(drv, value, + cs4231_chip->perchip_info.record.balance); + + return 0; +} + +static int cs4231_get_input_volume(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + return (int)cs4231_chip->perchip_info.record.gain; +} + +static int cs4231_set_output_volume(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + cs4231_play_gain(drv, value, cs4231_chip->perchip_info.play.balance); + + return 0; +} + +static int cs4231_get_output_volume(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + return cs4231_chip->perchip_info.play.gain; +} + +/* Likewise for balance */ +static int cs4231_set_input_balance(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + cs4231_chip->perchip_info.record.balance = value; + cs4231_record_gain(drv, cs4231_chip->perchip_info.record.gain, + cs4231_chip->perchip_info.record.balance); + + return 0; +} + +static int cs4231_get_input_balance(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + return (int)cs4231_chip->perchip_info.record.balance; +} + +static int cs4231_set_output_balance(struct sparcaudio_driver *drv, int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + cs4231_chip->perchip_info.play.balance = value; + cs4231_play_gain(drv, cs4231_chip->perchip_info.play.gain, + cs4231_chip->perchip_info.play.balance); + + return 0; +} + +static int cs4231_get_output_balance(struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + return (int)cs4231_chip->perchip_info.play.balance; } /* Set chip record gain */ -static unsigned int cs4231_record_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance) +static int cs4231_record_gain(struct sparcaudio_driver *drv, int value, unsigned char balance) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - unsigned int tmp = 0, r, l, ra, la; + int tmp = 0, r, l, r_adj, l_adj; unsigned char old_gain; r = l = value; if (balance < AUDIO_MID_BALANCE) { - r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT))); + r = (int)(value - ((AUDIO_MID_BALANCE - balance) + << AUDIO_BALANCE_SHIFT)); + if (r < 0) r = 0; } else if (balance > AUDIO_MID_BALANCE) { - l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT))); + l = (int)(value - ((balance - AUDIO_MID_BALANCE) + << AUDIO_BALANCE_SHIFT)); + if (l < 0) l = 0; } - la = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); - ra = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); + l_adj = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); + r_adj = r * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1); - cs4231_chip->pioregs->iar = 0x0; - old_gain = cs4231_chip->pioregs->idr; - cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, la); - cs4231_chip->pioregs->iar = 0x1; - old_gain = cs4231_chip->pioregs->idr; - cs4231_chip->pioregs->idr = RECGAIN_SET(old_gain, ra); + cs4231_chip->regs->iar = 0x0; + old_gain = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = RECGAIN_SET(old_gain, l_adj); + cs4231_chip->regs->iar = 0x1; + old_gain = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = RECGAIN_SET(old_gain, r_adj); if (l == value) { - (l == 0) ? (tmp = 0) : (tmp = ((la + 1) * AUDIO_MAX_GAIN) / (CS4231_MAX_GAIN + 1)); + (l == 0) ? (tmp = 0) : (tmp = ((l_adj + 1) * AUDIO_MAX_GAIN) / + (CS4231_MAX_GAIN + 1)); } else if (r == value) { - (r == 0) ? (tmp = 0) : (tmp = ((ra + 1) * AUDIO_MAX_GAIN) / (CS4231_MAX_GAIN + 1)); + (r == 0) ? (tmp = 0) : (tmp = ((r_adj + 1) * AUDIO_MAX_GAIN) / + (CS4231_MAX_GAIN + 1)); } - return (tmp); + cs4231_chip->perchip_info.record.gain = tmp; + return 0; } /* Set chip play gain */ -static unsigned int cs4231_play_gain(struct sparcaudio_driver *drv, unsigned int value, unsigned char balance) +static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned char balance) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - unsigned int tmp = 0, r, l, ra, la; + int tmp = 0, r, l, r_adj, l_adj; unsigned char old_gain; + tprintk(("in play_gain: %d %c\n", value, balance)); r = l = value; if (balance < AUDIO_MID_BALANCE) { - r = MAX(0, (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT))); + r = (int)(value - ((AUDIO_MID_BALANCE - balance) + << AUDIO_BALANCE_SHIFT)); + if (r < 0) r = 0; } else if (balance > AUDIO_MID_BALANCE) { - l = MAX(0, (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT))); + l = (int)(value - ((balance - AUDIO_MID_BALANCE) + << AUDIO_BALANCE_SHIFT)); + if (l < 0) l = 0; } - if (l == 0) { - la = CS4231_MAX_DEV_ATEN; - } else { - la = CS4231_MAX_ATEN - (l * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1)); - } - if (r == 0) { - ra = CS4231_MAX_DEV_ATEN; - } else { - ra = CS4231_MAX_ATEN - (r * (CS4231_MAX_ATEN + 1) / (AUDIO_MAX_GAIN + 1)); - } + (l == 0) ? (l_adj = CS4231_MAX_DEV_ATEN) : (l_adj = CS4231_MAX_ATEN - + (l * (CS4231_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); + (r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN - + (r * (CS4231_MAX_ATEN + 1) / + (AUDIO_MAX_GAIN + 1))); - cs4231_chip->pioregs->iar = 0x6; - old_gain = cs4231_chip->pioregs->idr; - cs4231_chip->pioregs->idr = GAIN_SET(old_gain, la); - cs4231_chip->pioregs->iar = 0x7; - old_gain = cs4231_chip->pioregs->idr; - cs4231_chip->pioregs->idr = GAIN_SET(old_gain, ra); + cs4231_chip->regs->iar = 0x6; + old_gain = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = GAIN_SET(old_gain, l_adj); + cs4231_chip->regs->iar = 0x7; + old_gain = cs4231_chip->regs->idr; + cs4231_chip->regs->idr = GAIN_SET(old_gain, r_adj); if ((value == 0) || (value == AUDIO_MAX_GAIN)) { tmp = value; } else { - if (l == value) { - tmp = ((CS4231_MAX_ATEN - la) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1)); - } else if (r == value) { - tmp = ((CS4231_MAX_ATEN - ra) * (AUDIO_MAX_GAIN + 1) / (CS4231_MAX_ATEN + 1)); - } + if (value == l) + tmp = ((CS4231_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) / + (CS4231_MAX_ATEN + 1)); + else if (r == value) + tmp = ((CS4231_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) / + (CS4231_MAX_ATEN + 1)); } - return (tmp); + cs4231_chip->perchip_info.play.gain = tmp; + + return 0; } /* Reset the audio chip to a sane state. */ -static void cs4231_reset(struct sparcaudio_driver *drv) +static void cs4231_chip_reset(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - cs4231_chip->dmaregs.dmacsr = CS_CHIP_RESET; - cs4231_chip->dmaregs.dmacsr = 0x00; - cs4231_chip->dmaregs.dmacsr |= CS_CDC_RESET; - - udelay(100); - - cs4231_chip->dmaregs.dmacsr &= ~(CS_CDC_RESET); - cs4231_chip->pioregs->iar |= IAR_AUTOCAL_BEGIN; + tprintk(("in cs4231_chip_reset\n")); + + cs4231_chip->regs->dmacsr = CS_CHIP_RESET; + cs4231_chip->regs->dmacsr = 0x00; + cs4231_chip->regs->dmacsr |= CS_CDC_RESET; - CHIP_BUG - - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x0c; - cs4231_chip->pioregs->idr = MISC_IR_MODE2; - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */ + udelay(20); - CHIP_BUG - - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; /* Ulaw */ + cs4231_chip->regs->dmacsr &= ~(CS_CDC_RESET); + cs4231_chip->regs->iar |= IAR_AUTOCAL_BEGIN; - CHIP_BUG + CHIP_READY - cs4231_chip->pioregs->iar = 0x19; - + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x0c; + cs4231_chip->regs->idr = MISC_IR_MODE2; + + /* This is the equivalent of DEFAULT_DATA_FMAT */ + cs4231_set_input_encoding(drv, AUDIO_ENCODING_ULAW); + cs4231_set_input_rate(drv, CS4231_RATE); + cs4231_set_input_channels(drv, CS4231_CHANNELS); + cs4231_set_input_precision(drv, CS4231_PRECISION); + + cs4231_set_output_encoding(drv, AUDIO_ENCODING_ULAW); + cs4231_set_output_rate(drv, CS4231_RATE); + cs4231_set_output_channels(drv, CS4231_CHANNELS); + cs4231_set_output_precision(drv, CS4231_PRECISION); + + cs4231_chip->regs->iar = 0x19; /* see what we can turn on */ - if (cs4231_chip->pioregs->idr & CS4231A) + if (cs4231_chip->regs->idr & CS4231A) { + tprintk(("This is a CS4231A\n")); cs4231_chip->status |= CS_STATUS_REV_A; - else + } else cs4231_chip->status &= ~CS_STATUS_REV_A; - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x10; - cs4231_chip->pioregs->idr = OLB_ENABLE; + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x10; + cs4231_chip->regs->idr = OLB_ENABLE; - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x11; + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x11; if (cs4231_chip->status & CS_STATUS_REV_A) - cs4231_chip->pioregs->idr = (HPF_ON | XTALE_ON); + cs4231_chip->regs->idr = (HPF_ON | XTALE_ON); else - cs4231_chip->pioregs->idr = (HPF_ON); + cs4231_chip->regs->idr = (HPF_ON); - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1a; - cs4231_chip->pioregs->idr = 0x00; + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1a; + cs4231_chip->regs->idr = 0x00; /* Now set things up for defaults */ - cs4231_chip->perchip_info.play.port = cs4231_out_port(drv, AUDIO_SPEAKER); - cs4231_chip->perchip_info.record.port = cs4231_in_port(drv, AUDIO_MICROPHONE); - cs4231_chip->perchip_info.play.gain = cs4231_play_gain(drv, CS4231_DEFAULT_PLAYGAIN, AUDIO_MID_BALANCE); - cs4231_chip->perchip_info.record.gain = cs4231_record_gain(drv, CS4231_DEFAULT_RECGAIN, AUDIO_MID_BALANCE); - cs4231_chip->perchip_info.monitor_gain = cs4231_monitor_gain(drv, LOOPB_OFF); + cs4231_set_input_balance(drv, AUDIO_MID_BALANCE); + cs4231_set_output_balance(drv, AUDIO_MID_BALANCE); + + cs4231_set_input_volume(drv, CS4231_DEFAULT_RECGAIN); + cs4231_set_output_volume(drv, CS4231_DEFAULT_PLAYGAIN); + + cs4231_set_input_port(drv, AUDIO_MICROPHONE); + cs4231_set_output_port(drv, AUDIO_SPEAKER); + + cs4231_set_monitor_volume(drv, LOOPB_OFF); - cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END; + cs4231_chip->regs->iar = IAR_AUTOCAL_END; cs4231_ready(drv); - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x09; - cs4231_chip->pioregs->idr &= ACAL_DISABLE; - cs4231_chip->pioregs->iar = (u_char)IAR_AUTOCAL_END; + cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x09; + cs4231_chip->regs->idr &= ACAL_DISABLE; + cs4231_chip->regs->iar = IAR_AUTOCAL_END; cs4231_ready(drv); cs4231_output_muted(drv, 0); + + cs4231_chip->recording_count = 0; + cs4231_chip->playing_count = 0; } -static void cs4231_mute(struct sparcaudio_driver *drv) +static int +cs4231_length_to_samplecount(struct audio_prinfo *thisdir, unsigned int length) { - struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + unsigned int count; - if (!(cs4231_chip->status & CS_STATUS_REV_A)) { - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN; - udelay(100); - cs4231_chip->pioregs->iar = IAR_AUTOCAL_END; - CHIP_BUG - } + if (thisdir->channels == 2) + count = (length/2); + else + count = length; + + if (thisdir->encoding == AUDIO_ENCODING_LINEAR) + count = (count/2); + else if (thisdir->encoding == AUDIO_ENCODING_DVI) + count = (count/4); + + return count; } -/* Not yet useful */ -#if 0 -static int cs4231_len_to_sample(struct sparcaudio_driver *drv, int length, int direction) +static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int length, unsigned int direction) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - int sample; + struct audio_prinfo *thisdir; + unsigned int count, nextcount, curcount; - if (/* number of channels == 2*/0) { - sample = (length/2); - } else { - sample = length; - } - if (/*encoding == AUDIO_ENCODING_LINEAR*/0) { - sample = sample/2; - } - return (sample); + if (direction == 1) /* record */ + { + thisdir = &cs4231_chip->perchip_info.record; + curcount = + cs4231_length_to_samplecount(thisdir, cs4231_chip->regs->dmacc); + nextcount = + cs4231_length_to_samplecount(thisdir, cs4231_chip->regs->dmacnc); + } + else /* play */ + { + thisdir = &cs4231_chip->perchip_info.play; + curcount = + cs4231_length_to_samplecount(thisdir, cs4231_chip->regs->dmapc); + nextcount = + cs4231_length_to_samplecount(thisdir, cs4231_chip->regs->dmapnc); + } + count = thisdir->samples; + length = cs4231_length_to_samplecount(thisdir, length); + thisdir->samples = ((count - nextcount) + (length - curcount)); } -#endif static int cs4231_open(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - /* Set the default audio parameters. */ - - cs4231_chip->perchip_info.play.sample_rate = CS4231_RATE; - cs4231_chip->perchip_info.play.channels = CS4231_CHANNELS; - cs4231_chip->perchip_info.play.precision = CS4231_PRECISION; - cs4231_chip->perchip_info.play.encoding = AUDIO_ENCODING_ULAW; - - cs4231_chip->perchip_info.record.sample_rate = CS4231_RATE; - cs4231_chip->perchip_info.record.channels = CS4231_CHANNELS; - cs4231_chip->perchip_info.record.precision = CS4231_PRECISION; - cs4231_chip->perchip_info.record.encoding = AUDIO_ENCODING_ULAW; - + /* Set the default audio parameters if not already in use. */ + if (file->f_mode & FMODE_WRITE) { + if (!(drv->flags & SDF_OPEN_WRITE) && + (cs4231_chip->perchip_info.play.active == 0)) { + cs4231_chip->perchip_info.play.open = 1; + cs4231_set_output_port(drv, AUDIO_SPEAKER); + } + } + + if (file->f_mode & FMODE_READ) { + if (!(drv->flags & SDF_OPEN_READ) && + (cs4231_chip->perchip_info.record.active == 0)) { + cs4231_chip->perchip_info.record.open = 1; + cs4231_set_input_port(drv, AUDIO_MICROPHONE); + } + } + cs4231_ready(drv); - cs4231_chip->status |= CS_STATUS_NEED_INIT; - - CHIP_BUG + CHIP_READY MOD_INC_USE_COUNT; @@ -422,31 +980,63 @@ static int cs4231_open(struct inode * inode, struct file * file, struct sparcaud static void cs4231_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) { + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + /* zero out any info about what data we have as well */ - /* should insert init on close variable optionally calling cs4231_reset() */ + + if (file->f_mode & FMODE_READ) + cs4231_chip->perchip_info.record.open = 0; + + if (file->f_mode & FMODE_WRITE) + cs4231_chip->perchip_info.play.open = 0; + + if (!cs4231_chip->perchip_info.play.open && + !cs4231_chip->perchip_info.record.open && + (cs4231_chip->status & CS_STATUS_INIT_ON_CLOSE)) { + cs4231_chip_reset(drv); + cs4231_chip->status &= ~CS_STATUS_INIT_ON_CLOSE; + } + MOD_DEC_USE_COUNT; } -static int cs4231_playintr(struct sparcaudio_driver *drv) +static void cs4231_playintr(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - /* Send the next byte of outgoing data. */ -#if 0 - if (cs4231_chip->output_ptr && cs4231_chip->output_count > 0) { - cs4231_chip->dmaregs.dmapnva = dma_handle; - cs4231_chip->dmaregs.dmapnc = length; - cs4231_chip->output_ptr++; - cs4231_chip->output_count--; - - /* Done with the buffer? Notify the midlevel driver. */ - if (cs4231_chip->output_count == 0) { + if (cs4231_chip->playlen == 0) + cs4231_chip->playlen = cs4231_chip->output_size; + + if (cs4231_chip->output_dma_handle) { + mmu_release_scsi_one((char *)cs4231_chip->output_dma_handle, + 4096, drv->dev->my_bus); + cs4231_chip->output_dma_handle = 0; + } + if (cs4231_chip->output_next_dma_handle) { + cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle; + cs4231_chip->output_next_dma_handle = 0; + } + + if (cs4231_chip->output_ptr && cs4231_chip->output_size > 0) { + cs4231_chip->output_next_dma_handle = + mmu_get_scsi_one((char *) cs4231_chip->output_ptr, 4096, + drv->dev->my_bus); + cs4231_chip->regs->dmapnva = cs4231_chip->output_next_dma_handle; + cs4231_chip->regs->dmapnc = cs4231_chip->output_size; + cs4231_chip->output_size = 0; cs4231_chip->output_ptr = NULL; - cs4231_chip->output_count = 0; - sparcaudio_output_done(drv); - } + cs4231_chip->playing_count++; } -#endif + + /* Get two buffers into the pipe, then chain... */ + if (cs4231_chip->playing_count < 3) + sparcaudio_output_done(drv, 0); + else { + cs4231_chip->playing_count--; + sparcaudio_output_done(drv, 1); + } + + return; } static void cs4231_recmute(int fmt) @@ -468,72 +1058,67 @@ static int cs4231_recintr(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - cs4231_recmute(cs4231_chip->perchip_info.record.encoding); - if (cs4231_chip->perchip_info.record.active == 0) { cs4231_pollinput(drv); - cs4231_chip->pioregs->iar = 0x9; - cs4231_chip->pioregs->idr &= CEN_DISABLE; - } - /* Read the next byte of incoming data. */ -#if 0 - if (cs4231_chip->input_ptr && cs4231_chip->input_count > 0) { - cs4231_chip->dmaregs.dmacnva = dma_handle; - cs4231_chip->dmaregs.dmacnc = length; - cs4231_chip->input_ptr++; - cs4231_chip->input_count--; - - /* Done with the buffer? Notify the midlevel driver. */ - if (cs4231_chip->input_count == 0) { - cs4231_chip->input_ptr = NULL; - cs4231_chip->input_count = 0; - sparcaudio_input_done(drv); - } + cs4231_recmute(cs4231_chip->perchip_info.record.encoding); + cs4231_disable_rec(drv); } -#endif + if (cs4231_chip->input_ptr) { + cs4231_chip->regs->dmacnva = (__u32) cs4231_chip->input_ptr; + cs4231_chip->regs->dmacnc = cs4231_chip->input_size; + cs4231_chip->input_ptr = NULL; + cs4231_chip->input_size = 0; + sparcaudio_input_done(drv); + } + return 1; } -static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count) +static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer, + unsigned long count) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - if (cs4231_chip->perchip_info.play.active || (cs4231_chip->perchip_info.play.pause)) + cs4231_chip->output_ptr = buffer; + cs4231_chip->output_size = count; + + if (cs4231_chip->perchip_info.play.active || + (cs4231_chip->perchip_info.play.pause)) return; cs4231_ready(drv); - if (cs4231_chip->status & CS_STATUS_NEED_INIT) - { - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; - - CHIP_BUG + cs4231_chip->perchip_info.play.active = 1; - cs4231_chip->status &= ~CS_STATUS_NEED_INIT; - } + cs4231_chip->playing_count = 0; + cs4231_disable_play(drv); + cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY; + cs4231_chip->regs->dmacsr &= ~CS_PPAUSE; + cs4231_playintr(drv); + cs4231_enable_play(drv); + cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP; - if (!cs4231_chip->perchip_info.play.pause) - { - /* init dma foo here */ - cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_PLAY; - cs4231_chip->dmaregs.dmacsr &= ~CS_PPAUSE; - if (cs4231_playintr(drv)) { - cs4231_chip->dmaregs.dmacsr |= CS_PLAY_SETUP; - cs4231_chip->pioregs->iar = 0x9; - cs4231_chip->pioregs->idr |= PEN_ENABLE; - } - } - cs4231_chip->perchip_info.play.active = 1; + cs4231_ready(drv); } static void cs4231_stop_output(struct sparcaudio_driver *drv) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + tprintk(("in cs4231_stop_output\n")); + cs4231_chip->output_ptr = NULL; + cs4231_chip->output_size = 0; + if (cs4231_chip->output_dma_handle) { + mmu_release_scsi_one((char *)cs4231_chip->output_dma_handle, + 4096, drv->dev->my_bus); + cs4231_chip->output_dma_handle = 0; + } + if (cs4231_chip->output_next_dma_handle) { + mmu_release_scsi_one((char *)cs4231_chip->output_next_dma_handle, + 4096, drv->dev->my_bus); + cs4231_chip->output_next_dma_handle = 0; + } cs4231_chip->perchip_info.play.active = 0; - cs4231_chip->dmaregs.dmacsr |= (CS_PPAUSE); + cs4231_chip->regs->dmacsr |= (CS_PPAUSE); } static void cs4231_pollinput(struct sparcaudio_driver *drv) @@ -541,44 +1126,31 @@ static void cs4231_pollinput(struct sparcaudio_driver *drv) struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; int x = 0; - while (!(cs4231_chip->dmaregs.dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) { + while (!(cs4231_chip->regs->dmacsr & CS_XINT_COVF) && x <= CS_TIMEOUT) { x++; } - cs4231_chip->dmaregs.dmacsr |= CS_XINT_CEMP; + cs4231_chip->regs->dmacsr |= CS_XINT_CEMP; } -static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count) +static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer, + unsigned long count) { struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; - if (cs4231_chip->perchip_info.record.active || (cs4231_chip->perchip_info.record.pause)) + if (cs4231_chip->perchip_info.record.active || + (cs4231_chip->perchip_info.record.pause)) return; cs4231_ready(drv); - if (cs4231_chip->status & CS_STATUS_NEED_INIT) - { - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x08; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; - cs4231_chip->pioregs->iar = IAR_AUTOCAL_BEGIN | 0x1c; - cs4231_chip->pioregs->idr = DEFAULT_DATA_FMAT; - - CHIP_BUG - - cs4231_chip->status &= ~CS_STATUS_NEED_INIT; - } - - if (!cs4231_chip->perchip_info.record.pause) - { - /* init dma foo here */ - cs4231_chip->dmaregs.dmacsr &= ~CS_XINT_CAPT; - cs4231_chip->dmaregs.dmacsr &= ~CS_CPAUSE; - cs4231_recintr(drv); - cs4231_chip->dmaregs.dmacsr |= CS_CAPT_SETUP; - cs4231_chip->pioregs->iar = 0x9; - cs4231_chip->pioregs->idr |= CEN_ENABLE; - } cs4231_chip->perchip_info.record.active = 1; + cs4231_chip->recording_count = 0; + /* init dma foo here */ + cs4231_chip->regs->dmacsr &= ~CS_XINT_CAPT; + cs4231_chip->regs->dmacsr &= ~CS_CPAUSE; + cs4231_recintr(drv); + cs4231_chip->regs->dmacsr |= CS_CAPT_SETUP; + cs4231_enable_rec(drv); } static void cs4231_stop_input(struct sparcaudio_driver *drv) @@ -586,50 +1158,174 @@ static void cs4231_stop_input(struct sparcaudio_driver *drv) struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; cs4231_chip->perchip_info.record.active = 0; - cs4231_chip->dmaregs.dmacsr |= (CS_CPAUSE); + cs4231_chip->regs->dmacsr |= (CS_CPAUSE); cs4231_pollinput(drv); /* need adjust the end pointer, process the input, and clean up the dma */ - cs4231_chip->pioregs->iar = 0x09; - cs4231_chip->pioregs->idr &= CEN_DISABLE; + cs4231_disable_rec(drv); } static void cs4231_audio_getdev(struct sparcaudio_driver *drv, audio_device_t * audinfo) { - strncpy(audinfo->name, "cs4231", sizeof(audinfo->name) - 1); - strncpy(audinfo->version, "x", sizeof(audinfo->version) - 1); - strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1); + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + strncpy(audinfo->name, "SUNW,CS4231", sizeof(audinfo->name) - 1); + /* versions: SPARCstation 4/5=a, Ultra=b */ + /* apparently Ultra 1, Ultra 2 don't have internal CD input */ + if (cs4231_chip->status & CS_STATUS_IS_ULTRA) + strncpy(audinfo->version, "b", sizeof(audinfo->version) - 1); + else + strncpy(audinfo->version, "a", sizeof(audinfo->version) - 1); + strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1); +} + + +static int cs4231_audio_getdev_sunos(struct sparcaudio_driver *drv) +{ + return AUDIO_DEV_CS4231; +} + +static void cs4231_loopback(struct sparcaudio_driver *drv, unsigned int value) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + + cs4231_chip->regs->iar = 0x0d; + cs4231_chip->regs->idr = (value ? LOOPB_ON : 0); +} + +static int cs4231_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg, + struct sparcaudio_driver *drv) +{ + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + int retval = 0; + + switch (cmd) { + case AUDIO_DIAG_LOOPBACK: + cs4231_chip->status |= CS_STATUS_INIT_ON_CLOSE; + cs4231_loopback(drv, (unsigned int)arg); + break; + default: + retval = -EINVAL; + } + + return retval; } -/* The ioctl handler should be expected to identify itself and handle loopback - mode */ -/* There will also be a handler for getinfo and setinfo */ +/* Audio interrupt handler. */ +void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; + struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private; + __u32 dummy; + + tprintk(("in cs4231_interrupt\n")); + + /* Clear the interrupt. */ + dummy = cs4231_chip->regs->dmacsr; + cs4231_chip->regs->dmacsr = dummy; + + /* now go through and figure out what gets to claim the interrupt + * if anything since we may be doing shared interrupts + */ + + if (dummy & CS_PLAY_INT) { + if (dummy & CS_XINT_PNVA) { + cs4231_chip->perchip_info.play.samples += + cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), + cs4231_chip->playlen); + cs4231_playintr(drv); + } + /* Any other conditions we need worry about? */ + } + + if (dummy & CS_CAPT_INT) { + if (dummy & CS_XINT_CNVA) { + cs4231_chip->perchip_info.record.samples += + cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.record), + cs4231_chip->reclen); + cs4231_recintr(drv); + } + /* Any other conditions we need worry about? */ + } + + if ((dummy & CS_XINT_CEMP) + && (cs4231_chip->perchip_info.record.active == 0)) + { + /* Fix me */ + cs4231_chip->perchip_info.record.active = 0; + } + if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) { + cs4231_chip->regs->dmacsr |= (CS_PPAUSE); + cs4231_disable_play(drv); + + cs4231_getsamplecount(drv, cs4231_chip->playlen, 0); + } + + if (dummy & CS_GENL_INT) { + /* If we get here we must be sharing an interrupt, but I haven't code + to handle this right now */ + } + +} static struct sparcaudio_operations cs4231_ops = { cs4231_open, cs4231_release, - NULL, /* cs4231_ioctl */ + cs4231_ioctl, cs4231_start_output, cs4231_stop_output, cs4231_start_input, cs4231_stop_input, cs4231_audio_getdev, + cs4231_set_output_volume, + cs4231_get_output_volume, + cs4231_set_input_volume, + cs4231_get_input_volume, + cs4231_set_monitor_volume, + cs4231_get_monitor_volume, + cs4231_set_output_balance, + cs4231_get_output_balance, + cs4231_set_input_balance, + cs4231_get_input_balance, + cs4231_set_output_channels, + cs4231_get_output_channels, + cs4231_set_input_channels, + cs4231_get_input_channels, + cs4231_set_output_precision, + cs4231_get_output_precision, + cs4231_set_input_precision, + cs4231_get_input_precision, + cs4231_set_output_port, + cs4231_get_output_port, + cs4231_set_input_port, + cs4231_get_input_port, + cs4231_set_output_encoding, + cs4231_get_output_encoding, + cs4231_set_input_encoding, + cs4231_get_input_encoding, + cs4231_set_output_rate, + cs4231_get_output_rate, + cs4231_set_input_rate, + cs4231_get_input_rate, + cs4231_audio_getdev_sunos, + cs4231_get_output_ports, + cs4231_get_input_ports, + cs4231_output_muted, + cs4231_get_output_muted, }; /* Attach to an cs4231 chip given its PROM node. */ -static inline int -cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev) +static int cs4231_attach(struct sparcaudio_driver *drv, + struct linux_sbus_device *sdev) { struct cs4231_chip *cs4231_chip; int err; struct linux_sbus *sbus = sdev->my_bus; -#ifdef __sparc_v9__ - struct devid_cookie dcookie; -#endif /* Allocate our private information structure. */ drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL); @@ -639,57 +1335,69 @@ cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev) /* Point at the information structure and initialize it. */ drv->ops = &cs4231_ops; cs4231_chip = (struct cs4231_chip *)drv->private; -#if 0 - cs4231_chip->input_ptr = NULL; - cs4231_chip->input_count = 0; - cs4231_chip->output_ptr = NULL; - cs4231_chip->output_count = 0; -#endif + cs4231_chip->input_ptr = cs4231_chip->output_ptr = NULL; + cs4231_chip->input_size = cs4231_chip->output_size = 0; + cs4231_chip->status = 0; + + drv->dev = sdev; /* Map the registers into memory. */ prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size; - cs4231_chip->pioregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, - sdev->reg_addrs[0].reg_size, - "cs4231", sdev->reg_addrs[0].which_io, 0); - if (!cs4231_chip->pioregs) { + cs4231_chip->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, + "cs4231", sdev->reg_addrs[0].which_io, + 0); + + if (!cs4231_chip->regs) { printk(KERN_ERR "cs4231: could not allocate registers\n"); kfree(drv->private); return -EIO; } - /* Reset the audio chip. */ - cs4231_reset(drv); - /* Attach the interrupt handler to the audio interrupt. */ - cs4231_chip->irq = sdev->irqs[0].pri; + cs4231_chip->irq = sdev->irqs[0]; -#ifndef __sparc_v9__ request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv); -#else - dcookie.real_dev_id = s; - dcookie.imap = dcookie.iclr = 0; - dcookie.pil = -1; - dcookie.bus_cookie = sdev->my_bus; - request_irq (cs4231_chip->irq, cs4231_interrupt, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "cs4231", drv); - cs4231_chip->irq = dcookie.ret_ino; -#endif enable_irq(cs4231_chip->irq); + cs4231_enable_interrupts(drv); + + /* Reset the audio chip. */ + cs4231_chip_reset(drv); + /* Register ourselves with the midlevel audio driver. */ err = register_sparcaudio_driver(drv); + if (err < 0) { printk(KERN_ERR "cs4231: unable to register\n"); + cs4231_disable_interrupts(drv); disable_irq(cs4231_chip->irq); free_irq(cs4231_chip->irq, drv); - sparc_free_io(cs4231_chip->pioregs, cs4231_chip->regs_size); + sparc_free_io(cs4231_chip->regs, cs4231_chip->regs_size); kfree(drv->private); return -EIO; } + cs4231_chip->perchip_info.play.active = + cs4231_chip->perchip_info.play.pause = 0; + + cs4231_chip->perchip_info.record.active = + cs4231_chip->perchip_info.record.pause = 0; + + cs4231_chip->perchip_info.play.avail_ports = (AUDIO_HEADPHONE | + AUDIO_SPEAKER | + AUDIO_LINE_OUT); + + cs4231_chip->perchip_info.record.avail_ports = (AUDIO_INTERNAL_CD_IN | + AUDIO_LINE_IN | + AUDIO_MICROPHONE | + AUDIO_ANALOG_LOOPBACK); + /* Announce the hardware to the user. */ - printk(KERN_INFO "cs4231 at 0x%lx irq %d\n", - (unsigned long)cs4231_chip->pioregs, cs4231_chip->irq); + printk(KERN_INFO "cs4231%c at 0x%lx irq %d\n", + (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ', + (unsigned long)cs4231_chip->regs, cs4231_chip->irq); /* Success! */ return 0; @@ -705,6 +1413,8 @@ __initfunc(int cs4231_init(void)) struct linux_sbus *bus; struct linux_sbus_device *sdev; + num_drivers = 0; + /* Probe each SBUS for cs4231 chips. */ for_all_sbusdev(sdev,bus) { if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { @@ -727,10 +1437,11 @@ static void cs4231_detach(struct sparcaudio_driver *drv) { struct cs4231_chip *info = (struct cs4231_chip *)drv->private; + cs4231_disable_interrupts(drv); unregister_sparcaudio_driver(drv); disable_irq(info->irq); free_irq(info->irq, drv); - sparc_free_io(info->pioregs, info->regs_size); + sparc_free_io(info->regs, info->regs_size); kfree(drv->private); } @@ -745,3 +1456,21 @@ void cleanup_module(void) } #endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/drivers/sbus/audio/cs4231.h b/drivers/sbus/audio/cs4231.h index cd1525051..569950062 100644 --- a/drivers/sbus/audio/cs4231.h +++ b/drivers/sbus/audio/cs4231.h @@ -10,65 +10,84 @@ #include <linux/types.h> +/* According to the CS4231A data provided on CS web site and sun's includes */ + struct cs4231_regs { - u_char iar; /* Index Address Register */ - u_char pad0[3]; - u_char idr; /* Indexed Data Register */ - u_char pad1[3]; - u_char statr; /* Status Register */ - u_char pad2[3]; - u_char piodr; /* PIO Data Register I/O */ - u_char pad3[3]; + __volatile__ __u8 iar; /* Index Address Register */ + __volatile__ __u8 pad0[3]; + __volatile__ __u8 idr; /* Indexed Data Register */ + __volatile__ __u8 pad1[3]; + __volatile__ __u8 statr; /* Status Register */ + __volatile__ __u8 pad2[3]; + __volatile__ __u8 piodr; /* PIO Data Register */ + __volatile__ __u8 pad3[3]; + __volatile__ __u32 dmacsr; /* APC CSR */ + __volatile__ __u32 dmapad[3]; + __volatile__ __u32 dmacva; /* Capture Virtual Address */ + __volatile__ __u32 dmacc; /* Capture Count */ + __volatile__ __u32 dmacnva; /* Capture Next Virtual Address */ + __volatile__ __u32 dmacnc; /* Capture Next Count */ + __volatile__ __u32 dmapva; /* Playback Virtual Address */ + __volatile__ __u32 dmapc; /* Playback Count */ + __volatile__ __u32 dmapnva; /* Playback Next Virtual Address */ + __volatile__ __u32 dmapnc; /* Playback Next Count */ }; -struct cs4231_dma { - u_long dmacsr; /* APC CSR */ - u_long dmapad[3]; - u_long dmacva; /* Capture Virtual Address */ - u_long dmacc; /* Capture Count */ - u_long dmacnva; /* Capture Next Virtual Address */ - u_long dmacnc; /* Capture Next Count */ - u_long dmapva; /* Playback Virtual Address */ - u_long dmapc; /* Playback Count */ - u_long dmapnva; /* Playback Next Virtual Address */ - u_long dmapnc; /* Playback Next Count */ -}; +/* Our structure for each chip */ struct cs4231_chip { - struct cs4231_regs *pioregs; - struct cs4231_dma dmaregs; - struct audio_info perchip_info; - int irq; - unsigned long regs_size; - - /* Keep track of various info */ - volatile unsigned int status; - - int dma; - int dma2; + struct cs4231_regs *regs; + struct audio_info perchip_info; + unsigned int playlen, reclen; + int irq; + unsigned long regs_size; + + /* Keep track of various info */ + volatile unsigned int status; + + /* Current buffer that the driver is playing. */ + volatile __u8 * output_ptr; + volatile unsigned long output_size; + volatile __u32 * output_dma_handle, output_next_dma_handle; + + /* Current record buffer. */ + volatile __u8 * input_ptr; + volatile unsigned long input_size; + + /* Number of buffers in the pipe. */ + volatile unsigned long playing_count; + volatile unsigned long recording_count; }; -/* Status bits */ +/* Local status bits */ #define CS_STATUS_NEED_INIT 0x01 #define CS_STATUS_INIT_ON_CLOSE 0x02 #define CS_STATUS_REV_A 0x04 +#define CS_STATUS_INTS_ON 0x08 +#define CS_STATUS_IS_ULTRA 0x10 #define CS_TIMEOUT 9000000 #define GAIN_SET(var, gain) ((var & ~(0x3f)) | gain) #define RECGAIN_SET(var, gain) ((var & ~(0x1f)) | gain) -#define IAR_AUTOCAL_BEGIN 0x40 /* IAR_MCE */ -#define IAR_AUTOCAL_END ~(0x40) /* IAR_MCD */ -#define IAR_NOT_READY 0x80 /* 80h not ready CODEC state */ +/* bits 0-3 set address of register accessed by idr register */ +/* bit 4 allows access to idr registers 16-31 in mode 2 only */ +/* bit 5 if set causes dma transfers to cease if the int bit of status set */ +#define IAR_AUTOCAL_BEGIN 0x40 /* MCE */ +#define IAR_NOT_READY 0x80 /* INIT */ -/* Each register assumed mode 1 and 2 unless noted */ +#define IAR_AUTOCAL_END ~(IAR_AUTOCAL_BEGIN) /* MCD */ + +/* Registers 1-15 modes 1 and 2. Registers 16-31 mode 2 only */ +/* Registers assumed to be same in both modes unless noted */ /* 0 - Left Input Control */ /* 1 - Right Input Control */ #define MIC_ENABLE(var) ((var & 0x2f) | 0x80) #define LINE_ENABLE(var) (var & 0x2f) #define CDROM_ENABLE(var) ((var & 0x2f) | 0x40) +#define OUTPUTLOOP_ENABLE(var) ((var & 0x2f) | 0xC0) #define INPUTCR_AUX1 0x40 /* 2 - Left Aux 1 Input Control */ @@ -85,31 +104,41 @@ struct cs4231_chip { #define CHANGE_DFR(var, val) ((var & ~(0xF)) | val) #define CHANGE_ENCODING(var, val) ((var & ~(0xe0)) | val) #define DEFAULT_DATA_FMAT CS4231_DFR_ULAW +#define CS4231_DFR_5512 0x01 +#define CS4231_DFR_6615 0x0f #define CS4231_DFR_8000 0x00 #define CS4231_DFR_9600 0x0e #define CS4231_DFR_11025 0x03 #define CS4231_DFR_16000 0x02 #define CS4231_DFR_18900 0x05 #define CS4231_DFR_22050 0x07 +#define CS4231_DFR_27429 0x04 #define CS4231_DFR_32000 0x06 +#define CS4231_DFR_33075 0x0d #define CS4231_DFR_37800 0x09 #define CS4231_DFR_44100 0x0b #define CS4231_DFR_48000 0x0c #define CS4231_DFR_LINEAR8 0x00 #define CS4231_DFR_ULAW 0x20 +#define CS4231_DFR_LINEARLE 0x40 #define CS4231_DFR_ALAW 0x60 -#define CS4231_DFR_ADPCM 0xa0 -#define CS4231_DFR_LINEARBE 0xc0 +#define CS4231_DFR_ADPCM 0xa0 /* N/A in mode 1 */ +#define CS4231_DFR_LINEARBE 0xc0 /* N/A in mode 1 */ #define CS4231_STEREO_ON(val) (val | 0x10) #define CS4231_MONO_ON(val) (val & ~0x10) /* 9 - Interface Config. Register */ -#define CHIP_INACTIVE 0x08 -#define PEN_ENABLE (0x01) +#define PEN_ENABLE (0x01) /* Playback Enable */ #define PEN_DISABLE (~0x01) -#define CEN_ENABLE (0x02) +#define CEN_ENABLE (0x02) /* Capture Enable */ #define CEN_DISABLE (~0x02) -#define ACAL_DISABLE (~0x08) +#define SDC_ENABLE (0x04) /* Turn on single DMA Channel mode */ +#define ACAL_CONV 0x08 /* Turn on converter autocal */ +#define ACAL_DISABLE (~0x08) +#define ACAL_DAC 0x10 /* Turn on DAC autocal */ +#define ACAL_FULL (ACAL_DAC|ACAL_CONV) /* Turn on full autocal */ +#define PPIO 0x20 /* do playback via PIO rather than DMA */ +#define CPIO 0x40 /* do capture via PIO rather than DMA */ #define ICR_AUTOCAL_INIT 0x01 /* 10 - Pin Control Register */ @@ -129,8 +158,8 @@ struct cs4231_chip { #define LOOPB_ON 0x01 #define LOOPB_OFF 0x00 -/* 14 - Unused (mode 1) */ -/* 15 - Unused (mode 1) */ +/* 14 - shared play/capture upper (mode 1) */ +/* 15 - shared play/capture lower (mode 1) */ /* 14 - Playback Upper (mode 2) */ /* 15 - Playback Lower (mode 2) */ @@ -138,11 +167,16 @@ struct cs4231_chip { /* The rest are mode 2 only */ /* 16 - Alternate Feature 1 Enable */ -#define OLB_ENABLE 0x80 +#define DAC_ZERO 0x01 +#define PLAY_MCE 0x10 +#define CAPTURE_MCE 0x20 +#define TIMER_ENABLE 0x40 +#define OLB_ENABLE 0x80 /* go to 2.88 vpp analog output */ /* 17 - Alternate Feature 2 Enable */ -#define HPF_ON 0x01 -#define XTALE_ON 0x20 +#define HPF_ON 0x01 /* High Pass Filter */ +#define XTALE_ON 0x02 /* Enable both crystals */ +#define APAR_OFF 0x04 /* ADPCM playback accum reset */ /* 18 - Left Line Input Gain */ /* 19 - Right Line Input Gain */ @@ -151,11 +185,18 @@ struct cs4231_chip { /* 21 - Timer Low */ /* 22 - unused */ -/* 23 - unused */ + +/* 23 - Alt. Fea. Ena 3 */ +#define ACF 0x01 /* 24 - Alternate Feature Status */ #define CS_PU 0x01 /* Underrun */ -#define CS_PO 0x20 /* Overrun */ +#define CS_PO 0x02 /* Overrun */ +#define CS_CU 0x04 /* Underrun */ +#define CS_CO 0x08 /* Overrun */ +#define CS_PI 0x10 +#define CS_CI 0x20 +#define CS_TI 0x40 /* 25 - Version */ #define CS4231A 0x20 @@ -163,7 +204,9 @@ struct cs4231_chip { /* 26 - Mono I/O Control */ #define CHANGE_MONO_GAIN(val) ((val & ~(0xFF)) | val) +#define MONO_IOCR_BYPASS 0x20 #define MONO_IOCR_MUTE 0x40 +#define MONO_IOCR_INMUTE 0x80 /* 27 - Unused */ @@ -176,29 +219,29 @@ struct cs4231_chip { /* 31 - Capture Lower */ /* Following are CSR register definitions for the Sparc */ -/* Also list "Solaris" equivs for now, not really useful tho */ -#define CS_INT_PENDING 0x800000 /* APC_IP */ /* Interrupt Pending */ -#define CS_PLAY_INT 0x400000 /* APC_PI */ /* Playback interrupt */ -#define CS_CAPT_INT 0x200000 /* APC_CI */ /* Capture interrupt */ -#define CS_GENL_INT 0x100000 /* APC_EI */ /* General interrupt */ -#define CS_XINT_ENA 0x80000 /* APC_IE */ /* General ext int. enable */ -#define CS_XINT_PLAY 0x40000 /* APC_PIE */ /* Playback ext intr */ -#define CS_XINT_CAPT 0x20000 /* APC_CIE */ /* Capture ext intr */ -#define CS_XINT_GENL 0x10000 /* APC_EIE */ /* Error ext intr */ -#define CS_XINT_EMPT 0x8000 /* APC_PMI */ /* Pipe empty interrupt */ -#define CS_XINT_PEMP 0x4000 /* APC_PM */ /* Play pipe empty */ -#define CS_XINT_PNVA 0x2000 /* APC_PD */ /* Playback NVA dirty */ -#define CS_XINT_PENA 0x1000 /* APC_PMIE */ /* play pipe empty Int enable */ -#define CS_XINT_COVF 0x800 /* APC_CM */ /* Cap data dropped on floor */ -#define CS_XINT_CNVA 0x400 /* APC_CD */ /* Capture NVA dirty */ -#define CS_XINT_CEMP 0x200 /* APC_CMI */ /* Capture pipe empty interrupt */ -#define CS_XINT_CENA 0x100 /* APC_CMIE */ /* Cap. pipe empty int enable */ -#define CS_PPAUSE 0x80 /* APC_PPAUSE */ /* Pause the play DMA */ -#define CS_CPAUSE 0x40 /* APC_CPAUSE */ /* Pause the capture DMA */ -#define CS_CDC_RESET 0x20 /* APC_CODEC_PDN */ /* CODEC RESET */ -#define PDMA_READY 0x08 /* PDMA_GO */ -#define CDMA_READY 0x04 /* CDMA_GO */ -#define CS_CHIP_RESET 0x01 /* APC_RESET */ /* Reset the chip */ + +#define CS_INT_PENDING 0x800000 /* Interrupt Pending */ +#define CS_PLAY_INT 0x400000 /* Playback interrupt */ +#define CS_CAPT_INT 0x200000 /* Capture interrupt */ +#define CS_GENL_INT 0x100000 /* General interrupt */ +#define CS_XINT_ENA 0x80000 /* General ext int. enable */ +#define CS_XINT_PLAY 0x40000 /* Playback ext intr */ +#define CS_XINT_CAPT 0x20000 /* Capture ext intr */ +#define CS_XINT_GENL 0x10000 /* Error ext intr */ +#define CS_XINT_EMPT 0x8000 /* Pipe empty interrupt */ +#define CS_XINT_PEMP 0x4000 /* Play pipe empty */ +#define CS_XINT_PNVA 0x2000 /* Playback NVA dirty */ +#define CS_XINT_PENA 0x1000 /* play pipe empty Int enable */ +#define CS_XINT_COVF 0x800 /* Cap data dropped on floor */ +#define CS_XINT_CNVA 0x400 /* Capture NVA dirty */ +#define CS_XINT_CEMP 0x200 /* Capture pipe empty interrupt */ +#define CS_XINT_CENA 0x100 /* Cap. pipe empty int enable */ +#define CS_PPAUSE 0x80 /* Pause the play DMA */ +#define CS_CPAUSE 0x40 /* Pause the capture DMA */ +#define CS_CDC_RESET 0x20 /* CODEC RESET */ +#define PDMA_READY 0x08 /* Play DMA Go */ +#define CDMA_READY 0x04 /* Capture DMA Go */ +#define CS_CHIP_RESET 0x01 /* Reset the chip */ #define CS_INIT_SETUP (CDMA_READY | PDMA_READY | CS_XINT_ENA | CS_XINT_PLAY | CS_XINT_GENL | CS_INT_PENDING | CS_PLAY_INT | CS_CAPT_INT | CS_GENL_INT) @@ -223,8 +266,5 @@ struct cs4231_chip { #define CS4231_CHANNELS (1) /* channels/sample */ #define CS4231_RATE (8000) /* default sample rate */ -/* Other rates supported are: - * 9600, 11025, 16000, 18900, 22050, 32000, 37800, 44100, 48000 - */ #endif |