summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/audio
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-01-04 16:03:48 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-01-04 16:03:48 +0000
commit78c388aed2b7184182c08428db1de6c872d815f5 (patch)
tree4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /drivers/sbus/audio
parenteb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff)
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'drivers/sbus/audio')
-rw-r--r--drivers/sbus/audio/Config.in2
-rw-r--r--drivers/sbus/audio/Makefile38
-rw-r--r--drivers/sbus/audio/amd7930.c359
-rw-r--r--drivers/sbus/audio/amd7930.h21
-rw-r--r--drivers/sbus/audio/audio.c2859
-rw-r--r--drivers/sbus/audio/cs4215.h32
-rw-r--r--drivers/sbus/audio/cs4231.c523
-rw-r--r--drivers/sbus/audio/cs4231.h5
-rw-r--r--drivers/sbus/audio/dbri.c977
-rw-r--r--drivers/sbus/audio/dbri.h37
10 files changed, 3617 insertions, 1236 deletions
diff --git a/drivers/sbus/audio/Config.in b/drivers/sbus/audio/Config.in
index 71a75c763..34f8124cd 100644
--- a/drivers/sbus/audio/Config.in
+++ b/drivers/sbus/audio/Config.in
@@ -9,4 +9,6 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Audio support (EXPERIMENTAL)' CONFIG_SPARCAUDIO
dep_tristate ' AMD7930 Lowlevel Driver' CONFIG_SPARCAUDIO_AMD7930 $CONFIG_SPARCAUDIO
dep_tristate ' CS4231 Lowlevel Driver' CONFIG_SPARCAUDIO_CS4231 $CONFIG_SPARCAUDIO
+ dep_tristate ' DBRI Lowlevel Driver' CONFIG_SPARCAUDIO_DBRI $CONFIG_SPARCAUDIO
+ dep_tristate ' Dummy Lowlevel Driver' CONFIG_SPARCAUDIO_DUMMY $CONFIG_SPARCAUDIO
fi
diff --git a/drivers/sbus/audio/Makefile b/drivers/sbus/audio/Makefile
index 3dcb06f82..cf38aa1d8 100644
--- a/drivers/sbus/audio/Makefile
+++ b/drivers/sbus/audio/Makefile
@@ -14,39 +14,61 @@
O_TARGET := sparcaudio.o
O_OBJS :=
M_OBJS :=
+M :=
+MM :=
ifeq ($(CONFIG_SPARCAUDIO),y)
-SBUS_AUDIO=y
+M=y
else
ifeq ($(CONFIG_SPARCAUDIO),m)
- SBUS_AUDIO_MODULE=y
+ MM=y
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y)
-SBUS_AUDIO=y
+M=y
OX_OBJS += amd7930.o
else
ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m)
- SBUS_AUDIO_MODULE=y
+ MM=y
MX_OBJS += amd7930.o
endif
endif
ifeq ($(CONFIG_SPARCAUDIO_CS4231),y)
-SBUS_AUDIO=y
+M=y
O_OBJS += cs4231.o
else
ifeq ($(CONFIG_SPARCAUDIO_CS4231),m)
- SBUS_AUDIO_MODULE=y
+ MM=y
M_OBJS += cs4231.o
endif
endif
-ifdef SBUS_AUDIO
+ifeq ($(CONFIG_SPARCAUDIO_DBRI),y)
+M=y
+OX_OBJS += dbri.o
+else
+ ifeq ($(CONFIG_SPARCAUDIO_DBRI),m)
+ MM=y
+ MX_OBJS += dbri.o
+ endif
+endif
+
+ifeq ($(CONFIG_SPARCAUDIO_DUMMY),y)
+M=y
+O_OBJS += dmy.o
+else
+ ifeq ($(CONFIG_SPARCAUDIO_DUMMY),m)
+ MM=y
+ M_OBJS += dmy.o
+ endif
+endif
+
+ifdef M
OX_OBJS += audio.o
else
- ifdef SBUS_AUDIO_MODULE
+ ifdef MM
MX_OBJS += audio.o
endif
endif
diff --git a/drivers/sbus/audio/amd7930.c b/drivers/sbus/audio/amd7930.c
index fb87bbe6f..4a0064f55 100644
--- a/drivers/sbus/audio/amd7930.c
+++ b/drivers/sbus/audio/amd7930.c
@@ -13,6 +13,74 @@
*
* Thanks to the AMD engineer who was able to get us the AMD79C30
* databook which has all the programming information and gain tables.
+ *
+ * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the
+ * SparcStation 1+. The chip provides microphone and speaker interfaces
+ * which provide mono-channel audio at 8K samples per second via either
+ * 8-bit A-law or 8-bit mu-law encoding. Also, the chip features an
+ * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface,
+ * which performs basic D channel LAPD processing and provides raw
+ * B channel data. The digital audio channel, the two ISDN B channels,
+ * and two 64 Kbps channels to the microprocessor are all interconnected
+ * via a multiplexer.
+ *
+ * This driver interfaces to the Linux HiSax ISDN driver, which performs
+ * all high-level Q.921 and Q.931 ISDN functions. The file is not
+ * itself a hardware driver; rather it uses functions exported by
+ * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio),
+ * allowing the chip to be simultaneously used for both audio and ISDN data.
+ * The hardware driver does _no_ buffering, but provides several callbacks
+ * which are called during interrupt service and should therefore run quickly.
+ *
+ * D channel transmission is performed by passing the hardware driver the
+ * address and size of an skb's data area, then waiting for a callback
+ * to signal successful transmission of the packet. A task is then
+ * queued to notify the HiSax driver that another packet may be transmitted.
+ *
+ * D channel reception is quite simple, mainly because of:
+ * 1) the slow speed of the D channel - 16 kbps, and
+ * 2) the presence of an 8- or 32-byte (depending on chip version) FIFO
+ * to buffer the D channel data on the chip
+ * Worst case scenario of back-to-back packets with the 8 byte buffer
+ * at 16 kbps yields an service time of 4 ms - long enough to preclude
+ * the need for fancy buffering. We queue a background task that copies
+ * data out of the receive buffer into an skb, and the hardware driver
+ * simply does nothing until we're done with the receive buffer and
+ * reset it for a new packet.
+ *
+ * B channel processing is more complex, because of:
+ * 1) the faster speed - 64 kbps,
+ * 2) the lack of any on-chip buffering (it interrupts for every byte), and
+ * 3) the lack of any chip support for HDLC encapsulation
+ *
+ * The HiSax driver can put each B channel into one of three modes -
+ * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay),
+ * and L1_MODE_HDLC (HDLC encapsulation by low-level driver).
+ * L1_MODE_HDLC is the most common, used for almost all "pure" digital
+ * data sessions. L1_MODE_TRANS is used for ISDN audio.
+ *
+ * HDLC B channel transmission is performed via a large buffer into
+ * which the skb is copied while performing HDLC bit-stuffing. A CRC
+ * is computed and attached to the end of the buffer, which is then
+ * passed to the low-level routines for raw transmission. Once
+ * transmission is complete, the hardware driver is set to enter HDLC
+ * idle by successive transmission of mark (all 1) bytes, waiting for
+ * the ISDN driver to prepare another packet for transmission and
+ * deliver it.
+ *
+ * HDLC B channel reception is performed via an X-byte ring buffer
+ * divided into N sections of X/N bytes each. Defaults: X=256 bytes, N=4.
+ * As the hardware driver notifies us that each section is full, we
+ * hand it the next section and schedule a background task to peruse
+ * the received section, bit-by-bit, with an HDLC decoder. As
+ * packets are detected, they are copied into a large buffer while
+ * decoding HDLC bit-stuffing. The ending CRC is verified, and if
+ * it is correct, we alloc a new skb of the correct length (which we
+ * now know), copy the packet into it, and hand it to the upper layers.
+ * Optimization: for large packets, we hand the buffer (which also
+ * happens to be an skb) directly to the upper layer after an skb_trim,
+ * and alloc a new large buffer for future packets, thus avoiding a copy.
+ * Then we return to HDLC processing; state is saved between calls.
*/
#include <linux/module.h>
@@ -22,6 +90,8 @@
#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/soundcard.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
@@ -32,6 +102,12 @@
#include <asm/audioio.h>
#include "amd7930.h"
+#if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+#include "../../isdn/hisax/hisax.h"
+#include "../../isdn/hisax/isdnl1.h"
+#include "../../isdn/hisax/foreign.h"
+#endif
+
#define MAX_DRIVERS 1
static struct sparcaudio_driver drivers[MAX_DRIVERS];
@@ -274,12 +350,6 @@ static void amd7930_update_map(struct sparcaudio_driver *drv)
map->gr = gx_coeff[level];
}
- /* force output to speaker for now */
- map->mmr2 |= AM_MAP_MMR2_LS;
-
- /* input from external microphone */
- map->mmr2 |= AM_MAP_MMR2_AINB;
-
amd7930_write_map(drv);
}
@@ -296,9 +366,6 @@ static void amd7930_update_map(struct sparcaudio_driver *drv)
* driver, since D.output_callback_arg is assumed to be a certain struct ptr
*/
-#include "../../isdn/hisax/hisax.h"
-#include "../../isdn/hisax/isdnl1.h"
-
#ifdef L2FRAME_DEBUG
inline void debug_info(struct amd7930_info *info, char c) {
@@ -453,7 +520,7 @@ static void transceive_Bchannel(struct amd7930_channel *channel,
channel->input_count = 0;
if (channel->input_callback)
(*channel->input_callback)
- (channel->input_callback_arg);
+ (channel->input_callback_arg, 1);
}
}
}
@@ -498,16 +565,7 @@ static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs)
static int amd7930_open(struct inode * inode, struct file * file,
struct sparcaudio_driver *drv)
{
- struct amd7930_info *info = (struct amd7930_info *)drv->private;
-
- /* Set the default audio parameters. */
- info->rgain = 128;
- info->pgain = 200;
- info->mgain = 0;
- amd7930_update_map(drv);
-
MOD_INC_USE_COUNT;
-
return 0;
}
@@ -640,6 +698,21 @@ static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
return AUDIO_DEV_AMD;
}
+static int amd7930_get_formats(struct sparcaudio_driver *drv)
+{
+ return (AFMT_MU_LAW | AFMT_A_LAW);
+}
+
+static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
+{
+ return (AUDIO_SPEAKER | AUDIO_HEADPHONE);
+}
+
+static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
+{
+ return (AUDIO_MICROPHONE);
+}
+
static int amd7930_set_output_volume(struct sparcaudio_driver *drv, int vol)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
@@ -681,6 +754,13 @@ static int amd7930_set_monitor_volume(struct sparcaudio_driver *drv, int vol)
return 0;
}
+static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ return info->mgain;
+}
+
/* Cheats. The amd has the minimum capabilities we support */
static int amd7930_get_output_balance(struct sparcaudio_driver *drv)
{
@@ -697,25 +777,68 @@ static int amd7930_get_output_channels(struct sparcaudio_driver *drv)
return AUDIO_MIN_PLAY_CHANNELS;
}
+static int amd7930_set_output_channels(struct sparcaudio_driver *drv,
+ int value)
+{
+ return (value == AUDIO_MIN_PLAY_CHANNELS) ? 0 : -EINVAL;
+}
+
static int amd7930_get_input_channels(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_REC_CHANNELS;
}
+static int
+amd7930_set_input_channels(struct sparcaudio_driver *drv, int value)
+{
+ return (value == AUDIO_MIN_REC_CHANNELS) ? 0 : -EINVAL;
+}
+
static int amd7930_get_output_precision(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_PLAY_PRECISION;
}
+static int
+amd7930_set_output_precision(struct sparcaudio_driver *drv, int value)
+{
+ return (value == AUDIO_MIN_PLAY_PRECISION) ? 0 : -EINVAL;
+}
+
static int amd7930_get_input_precision(struct sparcaudio_driver *drv)
{
return AUDIO_MIN_REC_PRECISION;
}
-/* This should eventually be made to DTRT, whatever that ends up */
+static int
+amd7930_set_input_precision(struct sparcaudio_driver *drv, int value)
+{
+ return (value == AUDIO_MIN_REC_PRECISION) ? 0 : -EINVAL;
+}
+
static int amd7930_get_output_port(struct sparcaudio_driver *drv)
{
- return AUDIO_SPEAKER; /* some of these have only HEADPHONE */
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+ if (info->map.mmr2 & AM_MAP_MMR2_LS)
+ return AUDIO_SPEAKER;
+ return AUDIO_HEADPHONE;
+}
+
+static int amd7930_set_output_port(struct sparcaudio_driver *drv, int value)
+{
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+ switch (value) {
+ case AUDIO_HEADPHONE:
+ info->map.mmr2 &= ~AM_MAP_MMR2_LS;
+ break;
+ case AUDIO_SPEAKER:
+ info->map.mmr2 |= AM_MAP_MMR2_LS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amd7930_update_map(drv);
+ return 0;
}
/* Only a microphone here, so no troubles */
@@ -724,15 +847,31 @@ static int amd7930_get_input_port(struct sparcaudio_driver *drv)
return AUDIO_MICROPHONE;
}
-/* This chip also supports AUDIO_ENCODING_ALAW, add support later */
-static int amd7930_get_output_encoding(struct sparcaudio_driver *drv)
+static int amd7930_get_encoding(struct sparcaudio_driver *drv)
{
- return AUDIO_ENCODING_ULAW;
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+ if (info->map.mmr1 & AM_MAP_MMR1_ALAW)
+ return AUDIO_ENCODING_ALAW;
+ return AUDIO_ENCODING_ULAW;
}
-static int amd7930_get_input_encoding(struct sparcaudio_driver *drv)
+static int
+amd7930_set_encoding(struct sparcaudio_driver *drv, int value)
{
- return AUDIO_ENCODING_ULAW;
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ switch (value) {
+ case AUDIO_ENCODING_ULAW:
+ info->map.mmr1 &= ~AM_MAP_MMR1_ALAW;
+ break;
+ case AUDIO_ENCODING_ALAW:
+ info->map.mmr1 |= AM_MAP_MMR1_ALAW;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amd7930_update_map(drv);
+ return 0;
}
/* This is what you get. Take it or leave it */
@@ -741,31 +880,55 @@ static int amd7930_get_output_rate(struct sparcaudio_driver *drv)
return AMD7930_RATE;
}
+static int
+amd7930_set_output_rate(struct sparcaudio_driver *drv, int value)
+{
+ return (value == AMD7930_RATE) ? 0 : -EINVAL;
+}
+
static int amd7930_get_input_rate(struct sparcaudio_driver *drv)
{
return AMD7930_RATE;
}
-static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
+static int
+amd7930_set_input_rate(struct sparcaudio_driver *drv, int value)
{
- return 0;
+ return (value == AMD7930_RATE) ? 0 : -EINVAL;
}
-static int amd7930_get_output_ports(struct sparcaudio_driver *drv)
+static int amd7930_get_output_muted(struct sparcaudio_driver *drv)
{
- return AUDIO_SPEAKER | AUDIO_HEADPHONE;
+ return 0;
}
-static int amd7930_get_input_ports(struct sparcaudio_driver *drv)
+static void amd7930_loopback(struct sparcaudio_driver *drv, unsigned int value)
{
- return AUDIO_MICROPHONE;
+ struct amd7930_info *info = (struct amd7930_info *)drv->private;
+
+ if (value)
+ info->map.mmr1 |= AM_MAP_MMR1_LOOPBACK;
+ else
+ info->map.mmr1 &= ~AM_MAP_MMR1_LOOPBACK;
+ amd7930_update_map(drv);
+ return;
}
-static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
+static int amd7930_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg,
+ struct sparcaudio_driver *drv)
{
- struct amd7930_info *info = (struct amd7930_info *)drv->private;
-
- return info->mgain;
+ int retval = 0;
+
+ switch (cmd) {
+ case AUDIO_DIAG_LOOPBACK:
+ amd7930_loopback(drv, (unsigned int)arg);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ return retval;
}
@@ -909,8 +1072,8 @@ static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv)
*
*/
-
-int amd7930_get_irqnum(int dev)
+#if defined (AMD79C30_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+static int amd7930_get_irqnum(int dev)
{
struct amd7930_info *info;
@@ -923,7 +1086,7 @@ int amd7930_get_irqnum(int dev)
return info->irq;
}
-int amd7930_get_liu_state(int dev)
+static int amd7930_get_liu_state(int dev)
{
struct amd7930_info *info;
@@ -936,7 +1099,7 @@ int amd7930_get_liu_state(int dev)
return info->liu_state;
}
-void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
+static void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -971,7 +1134,7 @@ void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
restore_flags(flags);
}
-void amd7930_liu_activate(int dev, int priority)
+static void amd7930_liu_activate(int dev, int priority)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1003,7 +1166,7 @@ void amd7930_liu_activate(int dev, int priority)
restore_flags(flags);
}
-void amd7930_liu_deactivate(int dev)
+static void amd7930_liu_deactivate(int dev)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1024,8 +1187,8 @@ void amd7930_liu_deactivate(int dev)
restore_flags(flags);
}
-void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
- void (*callback)(void *, int), void *callback_arg)
+static void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
+ void (*callback)(void *, int), void *callback_arg)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1069,9 +1232,9 @@ void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
restore_flags(flags);
}
-void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
- void (*callback)(void *, int, unsigned int),
- void *callback_arg)
+static void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1115,7 +1278,8 @@ void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
restore_flags(flags);
}
-int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
+static int amd7930_bopen(int dev, unsigned int chan,
+ int mode, u_char xmit_idle_char)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1124,6 +1288,10 @@ int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
return -1;
}
+ if (mode == L1_MODE_HDLC) {
+ return -1;
+ }
+
info = (struct amd7930_info *) drivers[dev].private;
save_and_cli(flags);
@@ -1167,7 +1335,7 @@ int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
return 0;
}
-void amd7930_bclose(int dev, int chan)
+static void amd7930_bclose(int dev, unsigned int chan)
{
struct amd7930_info *info;
register unsigned long flags;
@@ -1202,8 +1370,9 @@ void amd7930_bclose(int dev, int chan)
restore_flags(flags);
}
-void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
- void (*callback)(void *), void *callback_arg)
+static void amd7930_bxmit(int dev, unsigned int chan,
+ __u8 * buffer, unsigned long count,
+ void (*callback)(void *, int), void *callback_arg)
{
struct amd7930_info *info;
struct amd7930_channel *Bchan;
@@ -1228,8 +1397,10 @@ void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
}
}
-void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
- void (*callback)(void *), void *callback_arg)
+static void amd7930_brecv(int dev, unsigned int chan,
+ __u8 * buffer, unsigned long size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg)
{
struct amd7930_info *info;
struct amd7930_channel *Bchan;
@@ -1254,17 +1425,21 @@ void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
}
}
-EXPORT_SYMBOL(amd7930_get_irqnum);
-EXPORT_SYMBOL(amd7930_get_liu_state);
-EXPORT_SYMBOL(amd7930_liu_init);
-EXPORT_SYMBOL(amd7930_liu_activate);
-EXPORT_SYMBOL(amd7930_liu_deactivate);
-EXPORT_SYMBOL(amd7930_dxmit);
-EXPORT_SYMBOL(amd7930_drecv);
-EXPORT_SYMBOL(amd7930_bopen);
-EXPORT_SYMBOL(amd7930_bclose);
-EXPORT_SYMBOL(amd7930_bxmit);
-EXPORT_SYMBOL(amd7930_brecv);
+struct foreign_interface amd7930_foreign_interface = {
+ amd7930_get_irqnum,
+ amd7930_get_liu_state,
+ amd7930_liu_init,
+ amd7930_liu_activate,
+ amd7930_liu_deactivate,
+ amd7930_dxmit,
+ amd7930_drecv,
+ amd7930_bopen,
+ amd7930_bclose,
+ amd7930_bxmit,
+ amd7930_brecv
+};
+EXPORT_SYMBOL(amd7930_foreign_interface);
+#endif
/*
@@ -1274,7 +1449,7 @@ EXPORT_SYMBOL(amd7930_brecv);
static struct sparcaudio_operations amd7930_ops = {
amd7930_open,
amd7930_release,
- NULL, /* amd7930_ioctl */
+ amd7930_ioctl,
amd7930_start_output,
amd7930_stop_output,
amd7930_start_input,
@@ -1290,31 +1465,44 @@ static struct sparcaudio_operations amd7930_ops = {
amd7930_get_output_balance,
NULL, /* amd7930_set_input_balance */
amd7930_get_input_balance,
- NULL, /* amd7930_set_output_channels */
+ amd7930_set_output_channels,
amd7930_get_output_channels,
- NULL, /* amd7930_set_input_channels */
+ amd7930_set_input_channels,
amd7930_get_input_channels,
- NULL, /* amd7930_set_output_precision */
+ amd7930_set_output_precision,
amd7930_get_output_precision,
- NULL, /* amd7930_set_input_precision */
+ amd7930_set_input_precision,
amd7930_get_input_precision,
- NULL, /* amd7930_set_output_port */
+ amd7930_set_output_port,
amd7930_get_output_port,
NULL, /* amd7930_set_input_port */
amd7930_get_input_port,
- NULL, /* amd7930_set_output_encoding */
- amd7930_get_output_encoding,
- NULL, /* amd7930_set_input_encoding */
- amd7930_get_input_encoding,
- NULL, /* amd7930_set_output_rate */
+ amd7930_set_encoding,
+ amd7930_get_encoding,
+ amd7930_set_encoding,
+ amd7930_get_encoding,
+ amd7930_set_output_rate,
amd7930_get_output_rate,
- NULL, /* amd7930_set_input_rate */
+ amd7930_set_input_rate,
amd7930_get_input_rate,
amd7930_sunaudio_getdev_sunos,
amd7930_get_output_ports,
amd7930_get_input_ports,
- NULL, /* amd7930_set_output_muted */
+ NULL, /* amd7930_set_output_muted */
amd7930_get_output_muted,
+ NULL, /* amd7930_set_output_pause */
+ NULL, /* amd7930_get_output_pause */
+ NULL, /* amd7930_set_input_pause */
+ NULL, /* amd7930_get_input_pause */
+ NULL, /* amd7930_set_output_samples */
+ NULL, /* amd7930_get_output_samples */
+ NULL, /* amd7930_set_input_samples */
+ NULL, /* amd7930_get_input_samples */
+ NULL, /* amd7930_set_output_error */
+ NULL, /* amd7930_get_output_error */
+ NULL, /* amd7930_set_input_error */
+ NULL, /* amd7930_get_input_error */
+ amd7930_get_formats,
};
/* Attach to an amd7930 chip given its PROM node. */
@@ -1334,10 +1522,7 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
/* Point at the information structure and initialize it. */
drv->ops = &amd7930_ops;
info = (struct amd7930_info *)drv->private;
- info->Bb.output_ptr = info->Bb.input_ptr = NULL;
- info->Bb.output_count = info->Bb.input_count = 0;
- info->Bc.output_ptr = info->Bc.input_ptr = NULL;
- info->Bc.output_count = info->Bc.input_count = 0;
+ memset(info, 0, sizeof(*info));
info->ints_on = 1; /* force disable below */
drv->dev = sdev;
@@ -1376,9 +1561,17 @@ static int amd7930_attach(struct sparcaudio_driver *drv, int node,
memset(&info->map, 0, sizeof(info->map));
info->map.mmr1 = AM_MAP_MMR1_GX | AM_MAP_MMR1_GER
| AM_MAP_MMR1_GR | AM_MAP_MMR1_STG;
+ /* Start out with speaker, microphone */
+ info->map.mmr2 |= (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB);
+
+ /* Set the default audio parameters. */
+ info->rgain = 128;
+ info->pgain = 200;
+ info->mgain = 0;
+ amd7930_update_map(drv);
/* Register the amd7930 with the midlevel audio driver. */
- err = register_sparcaudio_driver(drv);
+ err = register_sparcaudio_driver(drv, 1);
if (err < 0) {
printk(KERN_ERR "amd7930: unable to register\n");
disable_irq(info->irq);
@@ -1402,7 +1595,7 @@ static void amd7930_detach(struct sparcaudio_driver *drv)
{
struct amd7930_info *info = (struct amd7930_info *)drv->private;
- unregister_sparcaudio_driver(drv);
+ unregister_sparcaudio_driver(drv, 1);
amd7930_idle(info);
disable_irq(info->irq);
free_irq(info->irq, drv);
diff --git a/drivers/sbus/audio/amd7930.h b/drivers/sbus/audio/amd7930.h
index 8df197061..76c4fd506 100644
--- a/drivers/sbus/audio/amd7930.h
+++ b/drivers/sbus/audio/amd7930.h
@@ -14,26 +14,7 @@
#define _AMD7930_H_
#include <linux/types.h>
-
-/* Exported ISDN functions */
-
-int amd7930_get_irqnum(int dev);
-int amd7930_get_liu_state(int dev);
-void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg);
-void amd7930_liu_activate(int dev, int priority);
-void amd7930_liu_deactivate(int dev);
-void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
- void (*callback)(void *, int), void *callback_arg);
-void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
- void (*callback)(void *, int, unsigned int),
- void *callback_arg);
-int amd7930_bopen(int dev, int chan, u_char xmit_idle_char);
-void amd7930_bclose(int dev, int chan);
-void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count,
- void (*callback)(void *), void *callback_arg);
-void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size,
- void (*callback)(void *), void *callback_arg);
-
+#include <linux/version.h>
/* Register interface presented to the CPU by the amd7930. */
struct amd7930
diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c
index c98923da4..36d925daf 100644
--- a/drivers/sbus/audio/audio.c
+++ b/drivers/sbus/audio/audio.c
@@ -1,13 +1,17 @@
/*
* 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) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright (C) 1997,1998 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)
+ * The mixer code cheats; Sparc hardware doesn't generally allow independent
+ * line control, and this fakes it badly.
*
+ * SNDCTL_DSP_SETFMT based on code contributed by
+ * Ion Badulescu (ionut@moisil.cs.columbia.edu)
*
* This is the audio midlayer that sits between the VFS character
* devices and the low-level audio hardware device drivers.
@@ -26,191 +30,345 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/soundcard.h>
-#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <asm/delay.h>
#include <asm/pgtable.h>
#include <asm/audioio.h>
+#undef __AUDIO_DEBUG
+#define __AUDIO_ERROR
+#undef __AUDIO_TRACE
+#ifdef __AUDIO_DEBUG
+#define dprintk(x) printk x
+#else
+#define dprintk(x)
+#endif
+#ifdef __AUDIO_ERROR
+#define eprintk(x) printk x
+#else
+#define eprintk(x)
+#endif
+#ifdef __AUDIO_TRACE
+#define tprintk(x) printk x
+#else
+#define tprintk(x)
+#endif
-/*
- * Low-level driver interface.
- */
+static short lis_get_elist_ent( strevent_t *list, pid_t pid );
+static int lis_add_to_elist( strevent_t **list, pid_t pid, short events );
+static int lis_del_from_elist( strevent_t **list, pid_t pid, short events );
+static void lis_free_elist( strevent_t **list);
+static void kill_procs( struct strevent *elist, int sig, short e);
+
+static struct sparcaudio_driver *drivers[SPARCAUDIO_MAX_DEVICES] = {NULL};
+
+/* This crap to be pulled off into a local include file */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
-/* We only support one low-level audio driver currently. */
-static struct sparcaudio_driver *driver = NULL;
+#define COPY_IN(arg, get) verify_area(VERIFY_READ, (void *)arg, sizeof(long)); memcpy_fromfs(&get, (long *)arg, sizeof(get));
+#define COPY_OUT(arg, ret) verify_area(VERIFY_WRITE, (void *)arg, sizeof(long)); memcpy_tofs((long *)arg, &ret, sizeof(ret));
+#define copy_to_user memcpy_tofs
+#define copy_from_user memcpy_fromfs
+#define signal_pending(x) (((x)->signal) & ~((x)->blocked))
-int register_sparcaudio_driver(struct sparcaudio_driver *drv)
+#else
+
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#define COPY_IN(arg, get) get_user(get, (int *)arg)
+#define COPY_OUT(arg, ret) put_user(ret, (int *)arg)
+#define sparcaudio_release_ret sparcaudio_release
+#define sparcaudioctl_release_ret sparcaudioctl_release
+#define sparcaudio_select sparcaudio_poll
+
+#endif
+
+int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
{
- int i;
+ int i, dev;
- /* If a driver is already present, don't allow the register. */
- if (driver)
- return -EIO;
+ /* If we've used up SPARCAUDIO_MAX_DEVICES, fail */
+ for (dev = 0; dev < SPARCAUDIO_MAX_DEVICES; dev++) {
+ if (drivers[dev] == NULL) {
+ break;
+ }
+ }
+ if (drivers[dev]) {
+ return -EIO;
+ }
/* Ensure that the driver has a proper operations structure. */
- if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output)
+ if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output ||
+ !drv->ops->start_input || !drv->ops->stop_input)
return -EINVAL;
- /* 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 < 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 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;
- drv->output_active = 0;
- drv->output_buffers = kmalloc(drv->num_output_buffers * sizeof(__u8 *), GFP_KERNEL);
- drv->output_sizes = kmalloc(drv->num_output_buffers * sizeof(size_t), GFP_KERNEL);
- if (!drv->output_buffers || !drv->output_sizes) goto kmalloc_failed1;
-
- /* Allocate the pages for each output buffer. */
- for (i = 0; i < drv->num_output_buffers; i++) {
- drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
- if (!drv->output_buffers[i]) goto kmalloc_failed2;
- }
+ /* 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 < output_buffer_size),
+ * 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 input_buffer_size
+ * bytes of audio data.
+ *
+ * TODO: Make number of input/output buffers tunable parameters
+ */
+
+ drv->num_output_buffers = 8;
+ drv->output_buffer_size = (4096 * 2);
+ drv->playing_count = 0;
+ drv->output_offset = 0;
+ drv->output_eof = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_active = 0;
+ drv->output_buffers = kmalloc(drv->num_output_buffers *
+ sizeof(__u8 *), GFP_KERNEL);
+ drv->output_sizes = kmalloc(drv->num_output_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ drv->output_notify = kmalloc(drv->num_output_buffers *
+ sizeof(char), GFP_KERNEL);
+ if (!drv->output_buffers || !drv->output_sizes || !drv->output_notify)
+ goto kmalloc_failed1;
+
+ drv->output_buffer = kmalloc((drv->output_buffer_size *
+ drv->num_output_buffers),
+ GFP_KERNEL);
+ if (!drv->output_buffer) goto kmalloc_failed2;
+
+ /* Allocate the pages for each output buffer. */
+ for (i = 0; i < drv->num_output_buffers; i++) {
+ drv->output_buffers[i] = (void *)(drv->output_buffer +
+ (i * drv->output_buffer_size));
+ drv->output_sizes[i] = 0;
+ drv->output_notify[i] = 0;
+ }
- /* 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;
- drv->input_active = 0;
- drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *), GFP_KERNEL);
- if (!drv->input_buffers) goto kmalloc_failed3;
-
- /* Allocate the pages for each input buffer. */
- for (i = 0; i < drv->num_input_buffers; i++) {
- drv->input_buffers[i] = (void *) __get_free_page(GFP_KERNEL);
- if (!drv->input_buffers[i]) goto kmalloc_failed4;
+ /* Setup the circular queue of input buffers. */
+ drv->num_input_buffers = 8;
+ drv->input_buffer_size = (4096 * 2);
+ drv->recording_count = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_offset = 0;
+ drv->input_size = 0;
+ drv->input_active = 0;
+ drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *),
+ GFP_KERNEL);
+ drv->input_sizes = kmalloc(drv->num_input_buffers *
+ sizeof(size_t), GFP_KERNEL);
+ if (!drv->input_buffers || !drv->input_sizes) goto kmalloc_failed3;
+
+ /* Allocate the pages for each input buffer. */
+ if (duplex == 1) {
+ drv->input_buffer = kmalloc((drv->input_buffer_size *
+ drv->num_input_buffers),
+ GFP_KERNEL);
+ if (!drv->input_buffer) goto kmalloc_failed4;
+
+ for (i = 0; i < drv->num_input_buffers; i++) {
+ drv->input_buffers[i] = (void *)(drv->input_buffer +
+ (i * drv->input_buffer_size));
+ }
+ } else {
+ if (duplex == 2) {
+ drv->input_buffer = drv->output_buffer;
+ drv->input_buffer_size = drv->output_buffer_size;
+ drv->num_input_buffers = drv->num_output_buffers;
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = drv->output_buffers[i];
+ } else {
+ for (i = 0; i < drv->num_input_buffers; i++)
+ drv->input_buffers[i] = NULL;
+ }
}
+ /* Take note of our duplexity */
+ drv->duplex = duplex;
+
/* Ensure that the driver is marked as not being open. */
drv->flags = 0;
MOD_INC_USE_COUNT;
- driver = drv;
- return 0;
+ /* Take driver slot, note which we took */
+ drv->index = dev;
+ drivers[dev] = drv;
+ return 0;
kmalloc_failed4:
- for (i--; i >= 0; i--)
- free_page((unsigned long) drv->input_buffers[i]);
+ kfree(drv->input_buffer);
kmalloc_failed3:
- if (drv->input_buffers)
- kfree(drv->input_buffers);
- i = drv->num_output_buffers;
+ if (drv->input_sizes)
+ kfree(drv->input_sizes);
+ if (drv->input_buffers)
+ kfree(drv->input_buffers);
+ i = drv->num_output_buffers;
kmalloc_failed2:
- for (i--; i >= 0; i--)
- free_page((unsigned long) drv->output_buffers[i]);
+ kfree(drv->output_buffer);
kmalloc_failed1:
- if (drv->output_buffers)
- kfree(drv->output_buffers);
- if (drv->output_sizes)
- kfree(drv->output_sizes);
-
- return -ENOMEM;
+ if (drv->output_buffers)
+ kfree(drv->output_buffers);
+ if (drv->output_sizes)
+ kfree(drv->output_sizes);
+ if (drv->output_notify)
+ kfree(drv->output_notify);
+
+ return -ENOMEM;
}
-int unregister_sparcaudio_driver(struct sparcaudio_driver *drv)
+int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
{
- int i;
-
- /* Make sure that the current driver is unregistering. */
- if (driver != drv)
+ /* Figure out which driver is unregistering */
+ if (drivers[drv->index] != drv)
return -EIO;
/* Deallocate the queue of output buffers. */
- for (i = 0; i < driver->num_output_buffers; i++)
- free_page((unsigned long) driver->output_buffers[i]);
- kfree(driver->output_buffers);
- kfree(driver->output_sizes);
+ kfree(drv->output_buffer);
+ kfree(drv->output_buffers);
+ kfree(drv->output_sizes);
+ kfree(drv->output_notify);
+
+ /* Deallocate the queue of input buffers. */
+ if (duplex == 1) {
+ kfree(drv->input_buffer);
+ kfree(drv->input_sizes);
+ }
+ kfree(drv->input_buffers);
- /* Deallocate the queue of input buffers. */
- for (i = 0; i < driver->num_input_buffers; i++)
- free_page((unsigned long) driver->input_buffers[i]);
- kfree(driver->input_buffers);
+ if (&(drv->sd_siglist) != NULL)
+ lis_free_elist( &(drv->sd_siglist) );
MOD_DEC_USE_COUNT;
- driver = NULL;
+ /* Null the appropriate driver */
+ drivers[drv->index] = NULL;
+
return 0;
}
-void sparcaudio_output_done(struct sparcaudio_driver * drv, int reclaim)
+void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
{
- /* 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++;
-
- /* 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;
-
- /* 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;
-
- /* Wake up any waiting writers or syncers and return. */
- wake_up_interruptible(&drv->output_write_wait);
- wake_up_interruptible(&drv->output_drain_wait);
- return;
- }
- }
-
- /* 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);
+ /*
+ * If !status, just restart current output.
+ * If status & 1, a buffer is finished; make it available again.
+ * If status & 2, a buffer was claimed for DMA and is still in use.
+ *
+ * The playing_count for non-DMA hardware should never be non-zero.
+ */
+ if (status & 1) {
+ if (drv->playing_count)
+ drv->playing_count--;
+ else {
+ drv->output_count--;
+ drv->output_size -= drv->output_sizes[drv->output_front];
+ if (drv->output_notify[drv->output_front] == 1) {
+ drv->output_eof++;
+ drv->output_notify[drv->output_front] = 0;
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+ }
+ drv->output_front = (drv->output_front + 1) %
+ drv->num_output_buffers;
+ }
+ }
+ if (status & 2) {
+ drv->output_count--;
+ drv->playing_count++;
+ drv->output_size -= drv->output_sizes[drv->output_front];
+ if (drv->output_notify[drv->output_front] == 1) {
+ drv->output_eof++;
+ drv->output_notify[drv->output_front] = 0;
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+ }
+ drv->output_front = (drv->output_front + 1) %
+ drv->num_output_buffers;
+ }
+
+ /* If we've played everything go inactive. */
+ if ((drv->output_count < 1) && (drv->playing_count < 1))
+ drv->output_active = 0;
+
+ /* If we got back a buffer, see if anyone wants to write to it */
+ if ((status & 1) || ((drv->output_count + drv->playing_count)
+ < drv->num_output_buffers))
+ wake_up_interruptible(&drv->output_write_wait);
+
+ /* If the output queue is empty, shut down the driver. */
+ if ((drv->output_count < 1) && (drv->playing_count < 1)) {
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+
+ /* Stop the lowlevel driver from outputing. */
+ /* drv->ops->stop_output(drv); Should not be necessary -- DJB 5/25/98 */
+ drv->output_active = 0;
+
+ /* Wake up any waiting writers or syncers and return. */
+ wake_up_interruptible(&drv->output_write_wait);
+ wake_up_interruptible(&drv->output_drain_wait);
+ return;
+ }
+
+ /* Start next block of output if we have it */
+ if (drv->output_count > 0) {
drv->ops->start_output(drv, drv->output_buffers[drv->output_front],
- drv->output_sizes[drv->output_front]);
-
+ drv->output_sizes[drv->output_front]);
+ drv->output_active = 1;
+ } else
+ drv->output_active = 0;
}
-void sparcaudio_input_done(struct sparcaudio_driver * drv)
+void sparcaudio_input_done(struct sparcaudio_driver * drv, int status)
{
- /* Point the queue after the "done" buffer. */
- drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
- drv->input_count++;
-
- /* If the input queue is full, shutdown the driver. */
- if (drv->input_count == drv->num_input_buffers) {
- /* Stop the lowlevel driver from inputing. */
- drv->ops->stop_input(drv);
- drv->input_active = 0;
- } else {
- /* Otherwise, give the driver the next buffer. */
- drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
- 4096);
- }
-
- /* Wake up any tasks that are waiting. */
- wake_up_interruptible(&drv->input_read_wait);
+ /* Deal with the weird case here */
+ if (drv->duplex == 2) {
+ if (drv->input_count < drv->num_input_buffers)
+ drv->input_count++;
+ drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
+ drv->input_buffer_size);
+ wake_up_interruptible(&drv->input_read_wait);
+ return;
+ }
+
+ /*
+ * If status % 2, they filled a buffer for us.
+ * If status & 2, they took a buffer from us.
+ */
+
+ if ((status % 2) == 1) {
+ drv->input_count++;
+ drv->recording_count--;
+ drv->input_size+=drv->input_buffer_size;
+ }
+
+ if (status > 1) {
+ drv->recording_count++;
+ drv->input_front = (drv->input_front + 1) % drv->num_input_buffers;
+ }
+
+ dprintk(("f%d r%d c%d u%d\n", drv->input_front, drv->input_rear, drv->input_count, drv->recording_count));
+ /* If the input queue is full, shutdown the driver. */
+ if ((drv->input_count + drv->recording_count) == drv->num_input_buffers) {
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+
+ /* Stop the lowlevel driver from inputing. */
+ drv->ops->stop_input(drv);
+ drv->input_active = 0;
+ } else {
+ /* Otherwise, give the driver the next buffer. */
+ drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
+ drv->input_buffer_size);
+ }
+
+ /* Wake up any tasks that are waiting. */
+ wake_up_interruptible(&drv->input_read_wait);
}
@@ -219,803 +377,1680 @@ void sparcaudio_input_done(struct sparcaudio_driver * drv)
* VFS layer interface
*/
-static loff_t sparcaudio_llseek(struct file * file, loff_t offset, int origin)
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static int sparcaudio_select(struct inode * inode, struct file * file,
+ int sel_type, select_table * wait)
{
- return -ESPIPE;
-}
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+
+ switch (sel_type) {
+ case SEL_IN:
+ if (((!file->f_flags & O_NONBLOCK) && drv->input_count) ||
+ (drv->input_size > drv->buffer_size)) {
+ dprintk(("read ready: c%d o%d\n", drv->input_count, drv->input_offset));
+ return 1;
+ }
+ select_wait(&drv->input_read_wait, wait);
+ break;
+ case SEL_OUT:
+ dprintk(("sel out: c%d o%d p%d\n", drv->output_count, drv->output_offset, drv->playing_count));
+ if ((drv->output_count + drv->playing_count) < (drv->num_output_buffers)) {
+ return 1;
+ }
+ select_wait(&drv->output_write_wait, wait);
+ break;
+ case SEL_EX:
+ break;
+ }
-static ssize_t sparcaudio_read(struct file * file,
- char *buf, size_t count, loff_t *ppos)
+ return 0;
+}
+#else
+static unsigned int sparcaudio_poll(struct file *file, poll_table * wait)
{
- int bytes_to_copy;
-
- if (! file->f_mode & FMODE_READ)
- return -EINVAL;
-
- if (driver->input_count == 0) {
- interruptible_sleep_on(&driver->input_read_wait);
- if (signal_pending(current))
- return -EINTR;
- }
+ unsigned int mask = 0;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+
+ poll_wait(file, &drv->input_read_wait, wait);
+ poll_wait(file, &drv->output_write_wait, wait);
+ if (((!file->f_flags & O_NONBLOCK) && drv->input_count) ||
+ (drv->input_size > drv->buffer_size)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if ((drv->output_count + drv->playing_count) < (drv->num_output_buffers)) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ return mask;
+}
+#endif
- bytes_to_copy = 4096 - driver->input_offset;
- if (bytes_to_copy > count)
- bytes_to_copy = count;
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static int sparcaudio_lseek(struct inode * inode, struct file * file,
+ off_t offset, int origin)
+#else
+static loff_t sparcaudio_lseek(struct file * file, loff_t offset, int origin)
+#endif
+{
+ return -ESPIPE;
+}
- copy_to_user_ret(buf, driver->input_buffers[driver->input_rear]+driver->input_offset,
- bytes_to_copy, -EFAULT);
- driver->input_offset += bytes_to_copy;
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static int sparcaudio_read(struct inode * inode, struct file * file,
+ char *buf, int count)
+#else
+static ssize_t sparcaudio_read(struct file * file, char *buf,
+ size_t count, loff_t *ppos)
+#endif
+{
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+ struct inode *inode = file->f_dentry->d_inode;
+#endif
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+ int bytes_to_copy, bytes_read = 0, err;
- if (driver->input_offset >= 4096) {
- driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers;
- driver->input_count--;
- driver->input_offset = 0;
- }
+ if (! file->f_mode & FMODE_READ)
+ return -EINVAL;
- return bytes_to_copy;
-}
+ if ((file->f_flags & O_NONBLOCK) && (drv->input_size < count))
+ return -EAGAIN;
+
+ while (count > 0) {
+ if (drv->input_count == 0) {
+ /* This *should* never happen. */
+ if (file->f_flags & O_NONBLOCK) {
+ printk("Warning: audio input leak!\n");
+ return -EAGAIN;
+ }
+ interruptible_sleep_on(&drv->input_read_wait);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+
+ bytes_to_copy = drv->input_buffer_size - drv->input_offset;
+
+ if (bytes_to_copy > count)
+ bytes_to_copy = count;
+
+ err = verify_area(VERIFY_WRITE, buf, bytes_to_copy);
+ if (err)
+ return err;
+
+ copy_to_user(buf, drv->input_buffers[drv->input_rear]+drv->input_offset,
+ bytes_to_copy);
+
+ drv->input_offset += bytes_to_copy;
+ drv->input_size -= bytes_to_copy;
+ buf += bytes_to_copy;
+ count -= bytes_to_copy;
+ bytes_read += bytes_to_copy;
+
+ if (drv->input_offset >= drv->input_buffer_size) {
+ drv->input_rear = (drv->input_rear + 1) %
+ drv->num_input_buffers;
+ drv->input_count--;
+ drv->input_offset = 0;
+ }
+ /* If we're in "loop audio" mode, try waking up the other side
+ * in case they're waiting for us to eat a block.
+ */
+ if (drv->duplex == 2) {
+ wake_up_interruptible(&drv->output_write_wait);
+ }
+ }
-static void sparcaudio_reorganize_buffers(struct sparcaudio_driver * driver)
-{
- /* It may never matter but if it does this routine will pack */
- /* buffers to free space for more data */
+ return bytes_read;
}
-static void sparcaudio_sync_output(struct sparcaudio_driver * driver)
+static void sparcaudio_sync_output(struct sparcaudio_driver * drv)
{
- unsigned long flags;
-
- /* If the low-level driver is not active, activate it. */
- save_and_cli(flags);
- 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);
+ unsigned long flags;
+
+ /* If the low-level driver is not active, activate it. */
+ save_and_cli(flags);
+ if ((!drv->output_active) && (drv->output_count > 0)) {
+ drv->ops->start_output(drv,
+ drv->output_buffers[drv->output_front],
+ drv->output_sizes[drv->output_front]);
+ drv->output_active = 1;
+ }
+ restore_flags(flags);
}
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static int sparcaudio_write(struct inode * inode, struct file * file,
+ const char *buf, int count)
+#else
static ssize_t sparcaudio_write(struct file * file, const char *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
+#endif
{
- int bytes_written = 0, bytes_to_copy;
-
- 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->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;
- }
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+ struct inode *inode = file->f_dentry->d_inode;
+#endif
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+ int bytes_written = 0, bytes_to_copy, err;
+
+ if (! file->f_mode & FMODE_WRITE)
+ return -EINVAL;
+
+ /*
+ * A signal they want notification when this is processed. Too bad
+ * sys_write doesn't tell us unless you patch it, in 2.0 kernels.
+ */
+ if (count == 0) {
+#ifndef notdef
+ drv->output_eof++;
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+#else
+ /* Nice code, but the world isn't ready yet... */
+ drv->output_notify[drv->output_rear] = 1;
+#endif
+ }
+
+ /* Loop until all output is written to device. */
+ while (count > 0) {
+ /* Check to make sure that an output buffer is available. */
+ if (drv->num_output_buffers == (drv->output_count + drv->playing_count)) {
+ /* We need buffers, so... */
+ sparcaudio_sync_output(drv);
+ if (file->f_flags & O_NONBLOCK) {
+ return -EAGAIN;
+ }
+ interruptible_sleep_on(&drv->output_write_wait);
+ if (signal_pending(current))
+ return bytes_written > 0 ? bytes_written : -EINTR;
+ }
- /* 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],
- 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;
- }
- sparcaudio_sync_output(driver);
+ /* No buffers were freed. Go back to sleep */
+ if (drv->num_output_buffers == (drv->output_count + drv->playing_count))
+ continue;
+
+ /* Deal with the weird case of a reader in the write area by trying to
+ * let them keep ahead of us... Go to sleep until they start servicing.
+ */
+ if ((drv->duplex == 2) && (drv->flags & SDF_OPEN_READ) &&
+ (drv->output_rear == drv->input_rear) && (drv->input_count > 0)) {
+ if (file->f_flags & O_NONBLOCK) {
+ return -EAGAIN;
+ }
+ interruptible_sleep_on(&drv->output_write_wait);
+ if (signal_pending(current))
+ return bytes_written > 0 ? bytes_written : -EINTR;
+ }
- /* Return the number of bytes written to the caller. */
- return bytes_written;
-}
+ /* Determine how much we can copy in this iteration. */
+ bytes_to_copy = count;
+ if (bytes_to_copy > drv->output_buffer_size - drv->output_offset)
+ bytes_to_copy = drv->output_buffer_size - drv->output_offset;
+
+ err = verify_area(VERIFY_READ, buf, bytes_to_copy);
+ if (err)
+ return err;
-#define COPY_IN(arg, get) get_user(get, (int *)arg)
-#define COPY_OUT(arg, ret) put_user(ret, (int *)arg)
+ copy_from_user(drv->output_buffers[drv->output_rear]+drv->output_offset, buf, bytes_to_copy);
+
+ /* Update the queue pointers. */
+ buf += bytes_to_copy;
+ count -= bytes_to_copy;
+ bytes_written += bytes_to_copy;
+
+ /* A block can get orphaned in a flush and not cleaned up. */
+ if (drv->output_offset)
+ drv->output_sizes[drv->output_rear] += bytes_to_copy;
+ else
+ drv->output_sizes[drv->output_rear] = bytes_to_copy;
+
+ drv->output_notify[drv->output_rear] = 0;
+
+ if (drv->output_sizes[drv->output_rear] == drv->output_buffer_size) {
+ drv->output_rear = (drv->output_rear + 1)
+ % drv->num_output_buffers;
+ drv->output_count++;
+ drv->output_offset = 0;
+ } else
+ drv->output_offset += bytes_to_copy;
+
+ drv->output_size+=bytes_to_copy;
+ }
+
+ sparcaudio_sync_output(drv);
+
+ /* Return the number of bytes written to the caller. */
+ return bytes_written;
+}
/* 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)
+#define MONO_DEVICES (SOUND_MASK_SPEAKER | SOUND_MASK_MIC)
-static inline int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
+static 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);
- }
- }
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+ unsigned long i = 0, j = 0, k = 0;
+
+ k = (unsigned long) &arg;
+
+ switch (cmd) {
+ case SOUND_MIXER_WRITE_RECLEV:
+ case SOUND_MIXER_WRITE_MIC:
+ case SOUND_MIXER_WRITE_CD:
+ case SOUND_MIXER_WRITE_LINE:
+ tprintk(("setting input volume (0x%x)", k));
+ if (drv->ops->get_input_channels)
+ j = drv->ops->get_input_channels(drv);
+ if (j == 1) {
+ i = s_to_m(k);
+ tprintk((" for mono to %d\n", i));
+ if (drv->ops->set_input_volume)
+ drv->ops->set_input_volume(drv, i);
+ if (drv->ops->get_input_volume)
+ i = drv->ops->get_input_volume(drv);
+ i = m_to_s(i);
+ } else {
+ i = s_to_g(k);
+ j = s_to_b(k);
+ tprintk((" for stereo to to %d (bal %d)\n", i, j));
+ if (drv->ops->set_input_volume)
+ drv->ops->set_input_volume(drv, i);
+ if (drv->ops->get_input_volume)
+ i = drv->ops->get_input_volume(drv);
+ if (drv->ops->set_input_balance)
+ drv->ops->set_input_balance(drv, j);
+ if (drv->ops->get_input_balance)
+ j = drv->ops->get_input_balance(drv);
+ i = b_to_s(i,j);
+ }
+ return COPY_OUT(arg, i);
+ case SOUND_MIXER_WRITE_PCM:
+ case SOUND_MIXER_WRITE_VOLUME:
+ case SOUND_MIXER_WRITE_SPEAKER:
+ tprintk(("setting output volume (0x%x)", k));
+ if (drv->ops->get_output_channels)
+ j = drv->ops->get_output_channels(drv);
+ if (j == 1) {
+ i = s_to_m(k);
+ tprintk((" for mono to %d\n", i));
+ if (drv->ops->set_output_volume)
+ drv->ops->set_output_volume(drv, i);
+ if (drv->ops->get_output_volume)
+ i = drv->ops->get_output_volume(drv);
+ i = m_to_s(i);
+ } else {
+ i = s_to_g(k);
+ j = s_to_b(k);
+ tprintk((" for stereo to to %d (bal %d)\n", i, j));
+ if (drv->ops->set_output_volume)
+ drv->ops->set_output_volume(drv, i);
+ if (drv->ops->get_output_volume)
+ i = drv->ops->get_output_volume(drv);
+ if (drv->ops->set_output_balance)
+ drv->ops->set_output_balance(drv, j);
+ if (drv->ops->get_output_balance)
+ j = drv->ops->get_output_balance(drv);
+ i = b_to_s(i,j);
+ }
+ return COPY_OUT(arg, i);
+ case SOUND_MIXER_READ_RECSRC:
+ if (drv->ops->get_input_port)
+ i = drv->ops->get_input_port(drv);
+ /* 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_WRITE_RECSRC:
+ if (!drv->ops->set_input_port)
+ return -EINVAL;
+ if (arg & SOUND_MASK_IMIX) j |= AUDIO_ANALOG_LOOPBACK;
+ if (arg & SOUND_MASK_CD) j |= AUDIO_CD;
+ if (arg & SOUND_MASK_LINE) j |= AUDIO_LINE_IN;
+ if (arg & SOUND_MASK_MIC) j |= AUDIO_MICROPHONE;
+ tprintk(("setting inport to %d\n", j));
+ i = drv->ops->set_input_port(drv, j);
+
+ return COPY_OUT(arg, i);
+ case SOUND_MIXER_READ_RECMASK:
+ if (drv->ops->get_input_ports)
+ i = drv->ops->get_input_ports(drv);
+ /* 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_READ_CAPS: /* mixer capabilities */
+ i = SOUND_CAP_EXCL_INPUT;
+ return COPY_OUT(arg, i);
+
+ case SOUND_MIXER_READ_DEVMASK: /* all supported devices */
+ case SOUND_MIXER_READ_STEREODEVS: /* what supports stereo */
+ if (drv->ops->get_input_ports)
+ i = drv->ops->get_input_ports(drv);
+ /* 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 (drv->ops->get_output_ports)
+ i = drv->ops->get_output_ports(drv);
+ 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 == SOUND_MIXER_READ_STEREODEVS)
+ j &= ~(MONO_DEVICES);
+ return COPY_OUT(arg, j);
+ case SOUND_MIXER_READ_PCM:
+ case SOUND_MIXER_READ_SPEAKER:
+ case SOUND_MIXER_READ_VOLUME:
+ if (drv->ops->get_output_channels)
+ j = drv->ops->get_output_channels(drv);
+ if (j == 1) {
+ if (drv->ops->get_output_volume)
+ i = drv->ops->get_output_volume(drv);
+ i = m_to_s(i);
+ } else {
+ if (drv->ops->get_output_volume)
+ i = drv->ops->get_output_volume(drv);
+ if (drv->ops->get_output_balance)
+ j = drv->ops->get_output_balance(drv);
+ i = b_to_s(i,j);
+ }
+ return COPY_OUT((int *)arg, i);
+ case SOUND_MIXER_READ_RECLEV:
+ case SOUND_MIXER_READ_MIC:
+ case SOUND_MIXER_READ_CD:
+ case SOUND_MIXER_READ_LINE:
+ if (drv->ops->get_input_channels)
+ j = drv->ops->get_input_channels(drv);
+ if (j == 1) {
+ if (drv->ops->get_input_volume)
+ i = drv->ops->get_input_volume(drv);
+ i = m_to_s(i);
+ } else {
+ if (drv->ops->get_input_volume)
+ i = drv->ops->get_input_volume(drv);
+ if (drv->ops->get_input_balance)
+ j = drv->ops->get_input_balance(drv);
+ i = b_to_s(i,j);
+ }
+ return COPY_OUT((int *)arg, i);
+ default:
+ return -EINVAL;
+ }
}
-static int sparcaudio_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
+/* AUDIO_SETINFO uses these to set values if possible. */
+static __inline__ int
+__sparcaudio_if_set_do(struct sparcaudio_driver *drv,
+ int (*set_function)(struct sparcaudio_driver *, int),
+ int (*get_function)(struct sparcaudio_driver *),
+ unsigned int value)
{
- 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:
- if (driver->output_count > 0) {
- interruptible_sleep_on(&driver->output_drain_wait);
- retval = signal_pending(current) ? -EINTR : 0;
- }
- 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;
-
- driver->ops->sunaudio_getdev(driver, &tmp);
-
- copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT);
- } else
- retval = -EINVAL;
-
- break;
-
- case AUDIO_GETDEV_SUNOS:
- if (driver->ops->sunaudio_getdev_sunos) {
- int tmp=driver->ops->sunaudio_getdev_sunos(driver);
-
- if (put_user(tmp, (int *)arg))
- retval = -EFAULT;
- } else
- retval = -EINVAL;
+ if (set_function && Modify(value))
+ return (int)set_function(drv, value);
+ else if (get_function)
+ return (int)get_function(drv);
+ else
+ return 0;
+}
- break;
+static __inline__ int
+__sparcaudio_if_setc_do(struct sparcaudio_driver *drv,
+ int (*set_function)(struct sparcaudio_driver *, int),
+ int (*get_function)(struct sparcaudio_driver *),
+ unsigned char value)
+{
+ if (set_function && Modifyc(value))
+ return (char)set_function(drv, (int)value);
+ else if (get_function)
+ return (char)get_function(drv);
+ else
+ return 0;
+}
- case AUDIO_GETINFO:
-
- AUDIO_INITINFO(&ainfo);
-
- if (driver->ops->get_input_rate)
- ainfo.record.sample_rate =
- driver->ops->get_input_rate(driver);
- if (driver->ops->get_input_channels)
- ainfo.record.channels =
- driver->ops->get_input_channels(driver);
- if (driver->ops->get_input_precision)
- ainfo.record.precision =
- driver->ops->get_input_precision(driver);
- if (driver->ops->get_input_encoding)
- ainfo.record.encoding =
- driver->ops->get_input_encoding(driver);
- if (driver->ops->get_input_volume)
- ainfo.record.gain =
- driver->ops->get_input_volume(driver);
- if (driver->ops->get_input_port)
- ainfo.record.port =
- driver->ops->get_input_port(driver);
- if (driver->ops->get_input_ports)
- ainfo.record.avail_ports =
- driver->ops->get_input_ports(driver);
- ainfo.record.buffer_size = 4096;
- ainfo.record.samples = 0;
- ainfo.record.eof = 0;
- ainfo.record.pause = 0;
- ainfo.record.error = 0;
- ainfo.record.waiting = 0;
- if (driver->ops->get_input_balance)
- ainfo.record.balance =
- driver->ops->get_input_balance(driver);
- ainfo.record.minordev = 4;
- ainfo.record.open = 1;
- ainfo.record.active = 0;
-
- if (driver->ops->get_output_rate)
- ainfo.play.sample_rate =
- driver->ops->get_output_rate(driver);
- if (driver->ops->get_output_channels)
- ainfo.play.channels =
- driver->ops->get_output_channels(driver);
- if (driver->ops->get_output_precision)
- ainfo.play.precision =
- driver->ops->get_output_precision(driver);
- if (driver->ops->get_output_encoding)
- ainfo.play.encoding =
- driver->ops->get_output_encoding(driver);
- if (driver->ops->get_output_volume)
- ainfo.play.gain =
- driver->ops->get_output_volume(driver);
- if (driver->ops->get_output_port)
- ainfo.play.port =
- driver->ops->get_output_port(driver);
- if (driver->ops->get_output_ports)
- ainfo.play.avail_ports =
- driver->ops->get_output_ports(driver);
- /* 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;
- ainfo.play.error = 0;
- ainfo.play.waiting = waitqueue_active(&driver->open_wait);
- if (driver->ops->get_output_balance)
- ainfo.play.balance =
- (unsigned char)driver->ops->get_output_balance(driver);
- ainfo.play.minordev = 4;
- ainfo.play.open = 1;
- ainfo.play.active = driver->output_active;
-
- if (driver->ops->get_monitor_volume)
- ainfo.monitor_gain =
- driver->ops->get_monitor_volume(driver);
-
- if (driver->ops->get_output_muted)
- ainfo.output_muted =
- (unsigned char)driver->ops->get_output_muted(driver);
-
- copy_to_user_ret((struct audio_info *)arg, &ainfo,
- sizeof(ainfo), -EFAULT);
+/* I_FLUSH, I_{G,S}ETSIG, I_NREAD provided for SunOS compatibility
+ *
+ * I must admit I'm quite ashamed of the state of the ioctl handling,
+ * but I do have several optimizations which I'm planning. -- DJB
+ */
- break;
+static int sparcaudio_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0, i, j, k;
+ int minor = MINOR(inode->i_rdev);
+ struct audio_info ainfo;
+ audio_buf_info binfo;
+ count_info cinfo;
+ struct sparcaudio_driver *drv =
+ drivers[(minor >> SPARCAUDIO_DEVICE_SHIFT)];
+
+ switch (minor & 0xf) {
+ case SPARCAUDIO_MIXER_MINOR:
+ return sparcaudio_mixer_ioctl(inode, file, cmd, arg);
+ case SPARCAUDIO_DSP16_MINOR:
+ case SPARCAUDIO_DSP_MINOR:
+ case SPARCAUDIO_AUDIO_MINOR:
+ case SPARCAUDIO_AUDIOCTL_MINOR:
+ switch (cmd) {
+ case I_GETSIG:
+ case I_GETSIG_SOLARIS:
+ j = (int)lis_get_elist_ent(drv->sd_siglist,current->pid);
+ COPY_OUT(arg, j);
+ retval = drv->input_count;
+ break;
- case AUDIO_SETINFO:
- {
- audio_info_t curinfo, newinfo;
-
- copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT);
-
- /* Without these there's no point in trying */
- if (!driver->ops->get_input_precision ||
- !driver->ops->get_input_channels ||
- !driver->ops->get_input_rate ||
- !driver->ops->get_input_encoding ||
- !driver->ops->get_output_precision ||
- !driver->ops->get_output_channels ||
- !driver->ops->get_output_rate ||
- !driver->ops->get_output_encoding)
- {
- retval = -EINVAL;
- break;
+ case I_SETSIG:
+ case I_SETSIG_SOLARIS:
+ if ((minor & 0xf) == SPARCAUDIO_AUDIOCTL_MINOR) {
+ if (!arg){
+ if (lis_del_from_elist(&(drv->sd_siglist),current->pid,S_ALL))
+ retval = -EINVAL;
+ else
+ if (!drv->sd_siglist)
+ drv->sd_sigflags=0;
}
-
- /* Do bounds checking for things which always apply.
- * Follow with enforcement of basic tenets of certain
- * encodings. Everything over and above generic is
- * enforced by the driver, which can assume that
- * Martian cases are taken care of here. */
- if (Modify(ainfo.play.gain) &&
- ((ainfo.play.gain > AUDIO_MAX_GAIN) ||
- (ainfo.play.gain < AUDIO_MIN_GAIN))) {
- /* Need to differentiate this from e.g. the above error */
+ else
+ if (lis_add_to_elist(&(drv->sd_siglist),current->pid,
+ (short)arg))
+ retval = -EAGAIN;
+ else
+ ((drv->sd_sigflags) |= (arg));
+ }
+ break;
+ case I_NREAD:
+ case I_NREAD_SOLARIS:
+ /* According to the Solaris man page, this copies out
+ * the size of the first streams buffer and returns
+ * the number of streams messages on the read queue as
+ * as its retval. (streamio(7I)) This should work. */
+
+ j = (drv->input_count > 0) ? drv->input_buffer_size : 0;
+ COPY_OUT(arg, j);
+ retval = drv->input_count;
+ break;
+ /*
+ * A poor substitute until we do true resizable buffers.
+ */
+ case SNDCTL_DSP_GETISPACE:
+ binfo.fragstotal = drv->num_input_buffers;
+ binfo.fragments = drv->num_input_buffers -
+ (drv->input_count + drv->recording_count);
+ binfo.fragsize = drv->input_buffer_size;
+ binfo.bytes = binfo.fragments*binfo.fragsize;
+
+ retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
+ if (retval) break;
+ copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
+ break;
+ case SNDCTL_DSP_GETOSPACE:
+ binfo.fragstotal = drv->num_output_buffers;
+ binfo.fragments = drv->num_output_buffers -
+ (drv->output_count + drv->playing_count +
+ (drv->output_offset ? 1 : 0));
+ binfo.fragsize = drv->output_buffer_size;
+ binfo.bytes = binfo.fragments*binfo.fragsize +
+ (drv->output_buffer_size - drv->output_offset);
+
+ retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
+ if (retval) break;
+ copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
+ break;
+ case SNDCTL_DSP_GETIPTR:
+ case SNDCTL_DSP_GETOPTR:
+ /*
+ * int bytes (number of bytes read/written since last)
+ * int blocks (number of frags read/wrote since last call)
+ * int ptr (current position of dma in buffer)
+ */
+ retval = 0;
+ cinfo.bytes = 0;
+ cinfo.ptr = 0;
+ cinfo.blocks = 0;
+ cinfo.bytes += cinfo.ptr;
+
+ retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(cinfo));
+ if (retval) break;
+ copy_to_user(&((char *)arg)[0], (char *)&cinfo, sizeof(cinfo));
+ break;
+ case SNDCTL_DSP_SETFRAGMENT:
+ /* XXX Small hack to get ESD/Enlightenment to work. --DaveM */
+ retval = 0;
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ /*
+ * I don't understand what I need to do yet.
+ */
+ retval = -EINVAL;
+ break;
+ case SNDCTL_DSP_SETTRIGGER:
+ /* This may not be 100% correct */
+ if ((arg & PCM_ENABLE_INPUT) && drv->ops->get_input_pause &&
+ drv->ops->set_input_pause) {
+ if (drv->ops->get_input_pause(drv))
+ drv->ops->set_input_pause(drv, 0);
+ } else {
+ if (!drv->ops->get_input_pause(drv))
+ drv->ops->set_input_pause(drv, 1);
+ }
+ if ((arg & PCM_ENABLE_OUTPUT) && drv->ops->get_output_pause &&
+ drv->ops->set_output_pause) {
+ if (drv->ops->get_output_pause(drv))
+ drv->ops->set_output_pause(drv, 0);
+ } else {
+ if (!drv->ops->get_output_pause(drv))
+ drv->ops->set_output_pause(drv, 1);
+ }
+ break;
+ case SNDCTL_DSP_GETTRIGGER:
+ j = 0;
+ if (drv->ops->get_input_pause)
+ if (drv->ops->get_input_pause(drv))
+ j = PCM_ENABLE_INPUT;
+ if (drv->ops->get_output_pause)
+ if (drv->ops->get_output_pause(drv))
+ j |= PCM_ENABLE_OUTPUT;
+ COPY_OUT(arg, j);
+ break;
+ case SNDCTL_DSP_GETBLKSIZE:
+ j = drv->input_buffer_size;
+ COPY_OUT(arg, j);
+ break;
+ case SNDCTL_DSP_SPEED:
+ if ((!drv->ops->set_output_rate) &&
+ (!drv->ops->set_input_rate)) {
retval = -EINVAL;
break;
}
- if (Modify(ainfo.record.gain) &&
- ((ainfo.record.gain > AUDIO_MAX_GAIN) ||
- (ainfo.record.gain < AUDIO_MIN_GAIN))) {
+ COPY_IN(arg, i);
+ tprintk(("setting speed to %d\n", i));
+ drv->ops->set_input_rate(drv, i);
+ drv->ops->set_output_rate(drv, i);
+ j = drv->ops->get_output_rate(drv);
+ COPY_OUT(arg, j);
+ break;
+ case SNDCTL_DSP_GETCAPS:
+ /*
+ * All Sparc audio hardware is full duplex.
+ * 4231 supports DMA pointer reading, 7930 is byte at a time.
+ * Pause functionality emulates trigger
+ */
+ j = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER | DSP_CAP_REALTIME;
+ COPY_OUT(arg, j);
+ break;
+ case SNDCTL_DSP_GETFMTS:
+ if (drv->ops->get_formats) {
+ j = drv->ops->get_formats(drv);
+ COPY_OUT(arg, j);
+ } else
retval = -EINVAL;
+ break;
+ case SNDCTL_DSP_SETFMT:
+ /* need to decode into encoding, precision */
+ COPY_IN(arg, i);
+
+ /* handle special case here */
+ if (i == AFMT_QUERY) {
+ j = drv->ops->get_output_encoding(drv);
+ k = drv->ops->get_output_precision(drv);
+ if (j == AUDIO_ENCODING_DVI)
+ i = AFMT_IMA_ADPCM;
+ else if (k == 8) {
+ switch (j) {
+ case AUDIO_ENCODING_ULAW:
+ i = AFMT_MU_LAW;
+ break;
+ case AUDIO_ENCODING_ALAW:
+ i = AFMT_A_LAW;
+ break;
+ case AUDIO_ENCODING_LINEAR8:
+ i = AFMT_U8;
+ break;
+ }
+ } else if (k == 16) {
+ case AUDIO_ENCODING_LINEAR:
+ i = AFMT_S16_BE;
+ break;
+ case AUDIO_ENCODING_LINEARLE:
+ i = AFMT_S16_LE;
+ break;
+ }
+ COPY_OUT(arg, i);
break;
}
- if (Modify(ainfo.monitor_gain) &&
- ((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
- (ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
+
+ /* Without these there's no point in trying */
+ if (!drv->ops->set_input_precision ||
+ !drv->ops->set_input_encoding ||
+ !drv->ops->set_output_precision ||
+ !drv->ops->set_output_encoding) {
+ eprintk(("missing set routines: failed\n"));
retval = -EINVAL;
break;
- }
- /* Don't need to check less than zero on these */
- if (Modifyc(ainfo.play.balance) &&
- (ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
+ }
+
+ if (drv->ops->get_formats)
+ if (!(drv->ops->get_formats(drv) & i)) {
+ dprintk(("format not supported\n"));
+ return -EINVAL;
+ }
+
+ switch (i) {
+ case AFMT_S16_LE:
+ ainfo.record.precision = ainfo.play.precision = 16;
+ ainfo.record.encoding = ainfo.play.encoding =
+ AUDIO_ENCODING_LINEARLE;
+ break;
+ case AFMT_S16_BE:
+ ainfo.record.precision = ainfo.play.precision = 16;
+ ainfo.record.encoding = ainfo.play.encoding =
+ AUDIO_ENCODING_LINEAR;
+ break;
+ case AFMT_MU_LAW:
+ ainfo.record.precision = ainfo.play.precision = 8;
+ ainfo.record.encoding = ainfo.play.encoding =
+ AUDIO_ENCODING_ULAW;
+ break;
+ case AFMT_A_LAW:
+ ainfo.record.precision = ainfo.play.precision = 8;
+ ainfo.record.encoding = ainfo.play.encoding =
+ AUDIO_ENCODING_ALAW;
+ break;
+ case AFMT_U8:
+ ainfo.record.precision = ainfo.play.precision = 8;
+ ainfo.record.encoding = ainfo.play.encoding =
+ AUDIO_ENCODING_LINEAR8;
+ break;
+ }
+ tprintk(("setting fmt to enc %d pr %d\n", ainfo.play.encoding,
+ ainfo.play.precision));
+ if ((drv->ops->set_input_precision(drv,
+ ainfo.record.precision)
+ < 0) ||
+ (drv->ops->set_output_precision(drv,
+ ainfo.play.precision)
+ < 0) ||
+ (drv->ops->set_input_encoding(drv,
+ ainfo.record.encoding)
+ < 0) ||
+ (drv->ops->set_output_encoding(drv,
+ ainfo.play.encoding)
+ < 0)) {
+ dprintk(("setting format: failed\n"));
+ return -EINVAL;
+ }
+ COPY_OUT(arg, i);
+ break;
+ case SNDCTL_DSP_CHANNELS:
+ if ((!drv->ops->set_output_channels) &&
+ (!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
- if (Modifyc(ainfo.record.balance) &&
- (ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
+ COPY_IN(arg, i);
+ drv->ops->set_input_channels(drv, i);
+ drv->ops->set_output_channels(drv, i);
+ i = drv->ops->get_output_channels(drv);
+ COPY_OUT(arg, i);
+ break;
+ case SNDCTL_DSP_STEREO:
+ if ((!drv->ops->set_output_channels) &&
+ (!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
+ COPY_IN(arg, i);
+ drv->ops->set_input_channels(drv, (i + 1));
+ drv->ops->set_output_channels(drv, (i + 1));
+ i = ((drv->ops->get_output_channels(drv)) - 1);
+ COPY_OUT(arg, i);
+ break;
+ case SNDCTL_DSP_POST:
+ case SNDCTL_DSP_SYNC:
+ case AUDIO_DRAIN:
+ /* Deal with weirdness so we can fill buffers */
+ if (drv->output_offset) {
+ drv->output_offset = 0;
+ drv->output_rear = (drv->output_rear + 1)
+ % drv->num_output_buffers;
+ drv->output_count++;
+ }
+ if (drv->output_count > 0) {
+ sparcaudio_sync_output(drv);
+ /* Only pause for DRAIN/SYNC, not POST */
+ if (cmd != SNDCTL_DSP_POST) {
+ interruptible_sleep_on(&drv->output_drain_wait);
+ retval = (signal_pending(current)) ? -EINTR : 0;
+ }
+ }
+ break;
+ case I_FLUSH:
+ case I_FLUSH_SOLARIS:
+ if (((unsigned int)arg == FLUSHW) ||
+ ((unsigned int)arg == FLUSHRW)) {
+ if (file->f_mode & FMODE_WRITE) {
+ sparcaudio_sync_output(drv);
+ if (drv->output_active) {
+ wake_up_interruptible(&drv->output_write_wait);
+ drv->ops->stop_output(drv);
+ }
+ drv->output_offset = 0;
+ drv->output_active = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_size = 0;
+ drv->playing_count = 0;
+ drv->output_eof = 0;
+ }
+ }
+ if (((unsigned int)arg == FLUSHR) ||
+ ((unsigned int)arg == FLUSHRW)) {
+ if (drv->input_active && (file->f_mode & FMODE_READ)) {
+ wake_up_interruptible(&drv->input_read_wait);
+ drv->ops->stop_input(drv);
+ drv->input_active = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_size = 0;
+ drv->input_offset = 0;
+ drv->recording_count = 0;
+ }
+ if ((file->f_mode & FMODE_READ) &&
+ (drv->flags & SDF_OPEN_READ)) {
+ if (drv->duplex == 2)
+ drv->input_count = drv->output_count;
+ drv->ops->start_input(drv,
+ drv->input_buffers[drv->input_front],
+ drv->input_buffer_size);
+ drv->input_active = 1;
+ }
+ }
+ if (((unsigned int)arg == FLUSHW) ||
+ ((unsigned int)arg == FLUSHRW)) {
+ if ((file->f_mode & FMODE_WRITE) &&
+ !(drv->flags & SDF_OPEN_WRITE)) {
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+ sparcaudio_sync_output(drv);
+ }
+ }
+ break;
+ case SNDCTL_DSP_RESET:
+ case AUDIO_FLUSH:
+ if (drv->output_active && (file->f_mode & FMODE_WRITE)) {
+ wake_up_interruptible(&drv->output_write_wait);
+ drv->ops->stop_output(drv);
+ drv->output_active = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_size = 0;
+ drv->playing_count = 0;
+ drv->output_offset = 0;
+ drv->output_eof = 0;
+ }
+ if (drv->input_active && (file->f_mode & FMODE_READ)) {
+ wake_up_interruptible(&drv->input_read_wait);
+ drv->ops->stop_input(drv);
+ drv->input_active = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_size = 0;
+ drv->input_offset = 0;
+ drv->recording_count = 0;
+ }
+ if ((file->f_mode & FMODE_READ) &&
+ !(drv->flags & SDF_OPEN_READ)) {
+ drv->ops->start_input(drv,
+ drv->input_buffers[drv->input_front],
+ drv->input_buffer_size);
+ drv->input_active = 1;
+ }
+ if ((file->f_mode & FMODE_WRITE) &&
+ !(drv->flags & SDF_OPEN_WRITE)) {
+ sparcaudio_sync_output(drv);
+ }
+ break;
+ case AUDIO_GETDEV:
+ if (drv->ops->sunaudio_getdev) {
+ audio_device_t tmp;
+
+ retval = verify_area(VERIFY_WRITE, (void *)arg,
+ sizeof(audio_device_t));
+ if (!retval)
+ drv->ops->sunaudio_getdev(drv, &tmp);
+ copy_to_user((audio_device_t *)arg, &tmp, sizeof(tmp));
+ } else
+ retval = -EINVAL;
+ break;
+ case AUDIO_GETDEV_SUNOS:
+ if (drv->ops->sunaudio_getdev_sunos) {
+ int tmp = drv->ops->sunaudio_getdev_sunos(drv);
+
+ retval = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
+ if (!retval)
+ copy_to_user((int *)arg, &tmp, sizeof(tmp));
+ } else
+ retval = -EINVAL;
+ break;
+ case AUDIO_GETINFO:
+ AUDIO_INITINFO(&ainfo);
+
+ if (drv->ops->get_input_rate)
+ ainfo.record.sample_rate =
+ drv->ops->get_input_rate(drv);
+ else
+ ainfo.record.sample_rate = (8000);
+ if (drv->ops->get_input_channels)
+ ainfo.record.channels =
+ drv->ops->get_input_channels(drv);
+ else
+ ainfo.record.channels = (1);
+ if (drv->ops->get_input_precision)
+ ainfo.record.precision =
+ drv->ops->get_input_precision(drv);
+ else
+ ainfo.record.precision = (8);
+ if (drv->ops->get_input_encoding)
+ ainfo.record.encoding =
+ drv->ops->get_input_encoding(drv);
+ else
+ ainfo.record.encoding = (AUDIO_ENCODING_ULAW);
+ if (drv->ops->get_input_volume)
+ ainfo.record.gain =
+ drv->ops->get_input_volume(drv);
+ else
+ ainfo.record.gain = (0);
+ if (drv->ops->get_input_port)
+ ainfo.record.port =
+ drv->ops->get_input_port(drv);
+ else
+ ainfo.record.port = (0);
+ if (drv->ops->get_input_ports)
+ ainfo.record.avail_ports =
+ drv->ops->get_input_ports(drv);
+ else
+ ainfo.record.avail_ports = (0);
+ /* To make e.g. vat happy, we let them think they control this */
+ ainfo.record.buffer_size = drv->buffer_size;
+ if (drv->ops->get_input_samples)
+ ainfo.record.samples = drv->ops->get_input_samples(drv);
+ else
+ ainfo.record.samples = 0;
+ /* This is undefined in the record context in Solaris */
+ ainfo.record.eof = 0;
+ if (drv->ops->get_input_pause)
+ ainfo.record.pause =
+ drv->ops->get_input_pause(drv);
+ else
+ ainfo.record.pause = 0;
+ if (drv->ops->get_input_error)
+ ainfo.record.error =
+ (unsigned char)drv->ops->get_input_error(drv);
+ else
+ ainfo.record.error = 0;
+ ainfo.record.waiting = 0;
+ if (drv->ops->get_input_balance)
+ ainfo.record.balance =
+ (unsigned char)drv->ops->get_input_balance(drv);
+ else
+ ainfo.record.balance = (unsigned char)(AUDIO_MID_BALANCE);
+ ainfo.record.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
+ ainfo.record.open = (drv->flags & SDF_OPEN_READ);
+ ainfo.record.active = 0;
+
+ if (drv->ops->get_output_rate)
+ ainfo.play.sample_rate =
+ drv->ops->get_output_rate(drv);
+ else
+ ainfo.play.sample_rate = (8000);
+ if (drv->ops->get_output_channels)
+ ainfo.play.channels =
+ drv->ops->get_output_channels(drv);
+ else
+ ainfo.play.channels = (1);
+ if (drv->ops->get_output_precision)
+ ainfo.play.precision =
+ drv->ops->get_output_precision(drv);
+ else
+ ainfo.play.precision = (8);
+ if (drv->ops->get_output_encoding)
+ ainfo.play.encoding =
+ drv->ops->get_output_encoding(drv);
+ else
+ ainfo.play.encoding = (AUDIO_ENCODING_ULAW);
+ if (drv->ops->get_output_volume)
+ ainfo.play.gain =
+ drv->ops->get_output_volume(drv);
+ else
+ ainfo.play.gain = (0);
+ if (drv->ops->get_output_port)
+ ainfo.play.port =
+ drv->ops->get_output_port(drv);
+ else
+ ainfo.play.port = (0);
+ if (drv->ops->get_output_ports)
+ ainfo.play.avail_ports =
+ drv->ops->get_output_ports(drv);
+ else
+ ainfo.play.avail_ports = (0);
+ /* This is not defined in the play context in Solaris */
+ ainfo.play.buffer_size = 0;
+ if (drv->ops->get_output_samples)
+ ainfo.play.samples = drv->ops->get_output_samples(drv);
+ else
+ ainfo.play.samples = 0;
+ ainfo.play.eof = drv->output_eof;
+ if (drv->ops->get_output_pause)
+ ainfo.play.pause =
+ drv->ops->get_output_pause(drv);
+ else
+ ainfo.play.pause = 0;
+ if (drv->ops->get_output_error)
+ ainfo.play.error =
+ (unsigned char)drv->ops->get_output_error(drv);
+ else
+ ainfo.play.error = 0;
+ ainfo.play.waiting = waitqueue_active(&drv->open_wait);
+ if (drv->ops->get_output_balance)
+ ainfo.play.balance =
+ (unsigned char)drv->ops->get_output_balance(drv);
+ else
+ ainfo.play.balance = (unsigned char)(AUDIO_MID_BALANCE);
+ ainfo.play.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
+ ainfo.play.open = (drv->flags & SDF_OPEN_WRITE);
+ ainfo.play.active = drv->output_active;
- /* If any of these changed, record them all, then make
- * changes atomically. If something fails, back it all out. */
- if (Modify(ainfo.record.precision) ||
- Modify(ainfo.record.sample_rate) ||
- Modify(ainfo.record.channels) ||
- Modify(ainfo.record.encoding) ||
- Modify(ainfo.play.precision) ||
- Modify(ainfo.play.sample_rate) ||
- Modify(ainfo.play.channels) ||
- Modify(ainfo.play.encoding))
- {
- /* If they're trying to change something we
- * have no routine for, they lose */
- if ((!driver->ops->set_input_encoding &&
- Modify(ainfo.record.encoding)) ||
- (!driver->ops->set_input_rate &&
- Modify(ainfo.record.sample_rate)) ||
- (!driver->ops->set_input_precision &&
- Modify(ainfo.record.precision)) ||
- (!driver->ops->set_input_channels &&
- Modify(ainfo.record.channels))) {
- retval = -EINVAL;
- break;
- }
-
- 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 (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;
+ if (drv->ops->get_monitor_volume)
+ ainfo.monitor_gain =
+ drv->ops->get_monitor_volume(drv);
+ else
+ ainfo.monitor_gain = (0);
+
+ if (drv->ops->get_output_muted)
+ ainfo.output_muted =
+ (unsigned char)drv->ops->get_output_muted(drv);
+ else
+ ainfo.output_muted = (unsigned char)(0);
+
+ retval = verify_area(VERIFY_WRITE, (void *)arg,
+ sizeof(struct audio_info));
+ if (retval < 0)
+ break;
- /* If they're trying to change something we
- * have no routine for, they lose */
- if ((!driver->ops->set_output_encoding &&
- Modify(ainfo.play.encoding)) ||
- (!driver->ops->set_output_rate &&
- Modify(ainfo.play.sample_rate)) ||
- (!driver->ops->set_output_precision &&
- Modify(ainfo.play.precision)) ||
- (!driver->ops->set_output_channels &&
- Modify(ainfo.play.channels))) {
+ copy_to_user((struct audio_info *)arg, &ainfo, sizeof(ainfo));
+
+ break;
+ case AUDIO_SETINFO:
+ {
+ audio_info_t curinfo, newinfo;
+
+ if (verify_area(VERIFY_READ, (audio_info_t *)arg,
+ sizeof(audio_info_t))) {
+ dprintk(("verify_area failed\n"));
+ return -EINVAL;
+ }
+ copy_from_user(&ainfo, (audio_info_t *)arg, sizeof(audio_info_t));
+
+ /* Without these there's no point in trying */
+ if (!drv->ops->get_input_precision ||
+ !drv->ops->get_input_channels ||
+ !drv->ops->get_input_rate ||
+ !drv->ops->get_input_encoding ||
+ !drv->ops->get_output_precision ||
+ !drv->ops->get_output_channels ||
+ !drv->ops->get_output_rate ||
+ !drv->ops->get_output_encoding)
+ {
+ eprintk(("missing get routines: failed\n"));
retval = -EINVAL;
break;
- }
-
- 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 (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;
+ }
- /* If we got this far, we're at least sane with
- * respect to generics. Try the changes. */
- if ((driver->ops->set_input_precision(driver, ainfo.record.precision) < 0) ||
- (driver->ops->set_output_precision(driver, ainfo.play.precision) < 0) ||
- (driver->ops->set_input_channels(driver, ainfo.record.channels) < 0) ||
- (driver->ops->set_output_channels(driver, ainfo.play.channels) < 0) ||
- (driver->ops->set_input_rate(driver, ainfo.record.sample_rate) < 0) ||
- (driver->ops->set_output_rate(driver, ainfo.play.sample_rate) < 0) ||
- (driver->ops->set_input_encoding(driver, ainfo.record.encoding) < 0) ||
- (driver->ops->set_output_encoding(driver, ainfo.play.encoding) < 0))
- {
- /* Pray we can set it all back. If not, uh... */
- driver->ops->set_input_precision(driver, curinfo.record.precision);
- driver->ops->set_output_precision(driver, curinfo.play.precision);
- driver->ops->set_input_channels(driver, curinfo.record.channels);
- driver->ops->set_output_channels(driver, curinfo.play.channels);
- driver->ops->set_input_rate(driver, curinfo.record.sample_rate);
- driver->ops->set_output_rate(driver, curinfo.play.sample_rate);
- driver->ops->set_input_encoding(driver, curinfo.record.encoding);
- driver->ops->set_output_encoding(driver, curinfo.play.encoding);
+ /* Do bounds checking for things which always apply.
+ * Follow with enforcement of basic tenets of certain
+ * encodings. Everything over and above generic is
+ * enforced by the driver, which can assume that
+ * Martian cases are taken care of here. */
+ if (Modify(ainfo.play.gain) &&
+ ((ainfo.play.gain > AUDIO_MAX_GAIN) ||
+ (ainfo.play.gain < AUDIO_MIN_GAIN))) {
+ /* Need to differentiate this from e.g. the above error */
+ eprintk(("play gain bounds: failed %d\n", ainfo.play.gain));
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.record.gain) &&
+ ((ainfo.record.gain > AUDIO_MAX_GAIN) ||
+ (ainfo.record.gain < AUDIO_MIN_GAIN))) {
+ eprintk(("rec gain bounds: failed %d\n", ainfo.record.gain));
+ retval = -EINVAL;
+ break;
+ }
+ if (Modify(ainfo.monitor_gain) &&
+ ((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
+ (ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
+ eprintk(("monitor gain bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ /* Don't need to check less than zero on these */
+ if (Modifyc(ainfo.play.balance) &&
+ (ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
+ eprintk(("play balance bounds: %d failed\n",
+ (int)ainfo.play.balance));
+ retval = -EINVAL;
+ break;
+ }
+ if (Modifyc(ainfo.record.balance) &&
+ (ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
+ eprintk(("rec balance bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+
+ /* If any of these changed, record them all, then make
+ * changes atomically. If something fails, back it all out. */
+ if (Modify(ainfo.record.precision) ||
+ Modify(ainfo.record.sample_rate) ||
+ Modify(ainfo.record.channels) ||
+ Modify(ainfo.record.encoding) ||
+ Modify(ainfo.play.precision) ||
+ Modify(ainfo.play.sample_rate) ||
+ Modify(ainfo.play.channels) ||
+ Modify(ainfo.play.encoding))
+ {
+ /* If they're trying to change something we
+ * have no routine for, they lose */
+ if ((!drv->ops->set_input_encoding &&
+ Modify(ainfo.record.encoding)) ||
+ (!drv->ops->set_input_rate &&
+ Modify(ainfo.record.sample_rate)) ||
+ (!drv->ops->set_input_precision &&
+ Modify(ainfo.record.precision)) ||
+ (!drv->ops->set_input_channels &&
+ Modify(ainfo.record.channels))) {
+ eprintk(("rec set no routines: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+
+ curinfo.record.encoding =
+ drv->ops->get_input_encoding(drv);
+ curinfo.record.sample_rate =
+ drv->ops->get_input_rate(drv);
+ curinfo.record.precision =
+ drv->ops->get_input_precision(drv);
+ curinfo.record.channels =
+ drv->ops->get_input_channels(drv);
+ 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 (newinfo.record.precision != 8) {
+ eprintk(("rec law precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.record.channels != 1) {
+ eprintk(("rec law channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ case AUDIO_ENCODING_LINEARLE:
+ if (newinfo.record.precision != 16) {
+ eprintk(("rec lin precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.record.channels != 1 &&
+ newinfo.record.channels != 2)
+ {
+ eprintk(("rec lin channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR8:
+ if (newinfo.record.precision != 8) {
+ eprintk(("rec lin8 precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.record.channels != 1 &&
+ newinfo.record.channels != 2)
+ {
+ eprintk(("rec lin8 channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ if (retval < 0)
+ break;
+
+ /* If they're trying to change something we
+ * have no routine for, they lose */
+ if ((!drv->ops->set_output_encoding &&
+ Modify(ainfo.play.encoding)) ||
+ (!drv->ops->set_output_rate &&
+ Modify(ainfo.play.sample_rate)) ||
+ (!drv->ops->set_output_precision &&
+ Modify(ainfo.play.precision)) ||
+ (!drv->ops->set_output_channels &&
+ Modify(ainfo.play.channels))) {
+ eprintk(("play set no routine: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+
+ curinfo.play.encoding =
+ drv->ops->get_output_encoding(drv);
+ curinfo.play.sample_rate =
+ drv->ops->get_output_rate(drv);
+ curinfo.play.precision =
+ drv->ops->get_output_precision(drv);
+ curinfo.play.channels =
+ drv->ops->get_output_channels(drv);
+ 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 (newinfo.play.precision != 8) {
+ eprintk(("play law precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.play.channels != 1) {
+ eprintk(("play law channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ case AUDIO_ENCODING_LINEARLE:
+ if (newinfo.play.precision != 16) {
+ eprintk(("play lin precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.play.channels != 1 &&
+ newinfo.play.channels != 2)
+ {
+ eprintk(("play lin channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_LINEAR8:
+ if (newinfo.play.precision != 8) {
+ eprintk(("play lin8 precision bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
+ if (newinfo.play.channels != 1 &&
+ newinfo.play.channels != 2)
+ {
+ eprintk(("play lin8 channel bounds: failed\n"));
+ retval = -EINVAL;
+ break;
+ }
}
+
+ if (retval < 0)
+ break;
+
+ /* If we got this far, we're at least sane with
+ * respect to generics. Try the changes. */
+ if ((drv->ops->set_input_channels &&
+ (drv->ops->set_input_channels(drv,
+ newinfo.record.channels)
+ < 0)) ||
+ (drv->ops->set_output_channels &&
+ (drv->ops->set_output_channels(drv,
+ newinfo.play.channels)
+ < 0)) ||
+ (drv->ops->set_input_rate &&
+ (drv->ops->set_input_rate(drv,
+ newinfo.record.sample_rate)
+ < 0)) ||
+ (drv->ops->set_output_rate &&
+ (drv->ops->set_output_rate(drv,
+ newinfo.play.sample_rate)
+ < 0)) ||
+ (drv->ops->set_input_precision &&
+ (drv->ops->set_input_precision(drv,
+ newinfo.record.precision)
+ < 0)) ||
+ (drv->ops->set_output_precision &&
+ (drv->ops->set_output_precision(drv,
+ newinfo.play.precision)
+ < 0)) ||
+ (drv->ops->set_input_encoding &&
+ (drv->ops->set_input_encoding(drv,
+ newinfo.record.encoding)
+ < 0)) ||
+ (drv->ops->set_output_encoding &&
+ (drv->ops->set_output_encoding(drv,
+ newinfo.play.encoding)
+ < 0)))
+ {
+ dprintk(("setting format: failed\n"));
+ /* Pray we can set it all back. If not, uh... */
+ if (drv->ops->set_input_channels)
+ drv->ops->set_input_channels(drv,
+ curinfo.record.channels);
+ if (drv->ops->set_output_channels)
+ drv->ops->set_output_channels(drv,
+ curinfo.play.channels);
+ if (drv->ops->set_input_rate)
+ drv->ops->set_input_rate(drv,
+ curinfo.record.sample_rate);
+ if (drv->ops->set_output_rate)
+ drv->ops->set_output_rate(drv,
+ curinfo.play.sample_rate);
+ if (drv->ops->set_input_precision)
+ drv->ops->set_input_precision(drv,
+ curinfo.record.precision);
+ if (drv->ops->set_output_precision)
+ drv->ops->set_output_precision(drv,
+ curinfo.play.precision);
+ if (drv->ops->set_input_encoding)
+ drv->ops->set_input_encoding(drv,
+ curinfo.record.encoding);
+ if (drv->ops->set_output_encoding)
+ drv->ops->set_output_encoding(drv,
+ curinfo.play.encoding);
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ if (retval < 0)
+ break;
+
+ newinfo.record.balance =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_input_balance,
+ drv->ops->get_input_balance,
+ ainfo.record.balance);
+ newinfo.play.balance =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_output_balance,
+ drv->ops->get_output_balance,
+ ainfo.play.balance);
+ newinfo.record.error =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_input_error,
+ drv->ops->get_input_error,
+ ainfo.record.error);
+ newinfo.play.error =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_output_error,
+ drv->ops->get_output_error,
+ ainfo.play.error);
+ newinfo.output_muted =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_output_muted,
+ drv->ops->get_output_muted,
+ ainfo.output_muted);
+ newinfo.record.gain =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_input_volume,
+ drv->ops->get_input_volume,
+ ainfo.record.gain);
+ newinfo.play.gain =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_output_volume,
+ drv->ops->get_output_volume,
+ ainfo.play.gain);
+ newinfo.record.port =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_input_port,
+ drv->ops->get_input_port,
+ ainfo.record.port);
+ newinfo.play.port =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_output_port,
+ drv->ops->get_output_port,
+ ainfo.play.port);
+ newinfo.record.samples =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_input_samples,
+ drv->ops->get_input_samples,
+ ainfo.record.samples);
+ newinfo.play.samples =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_output_samples,
+ drv->ops->get_output_samples,
+ ainfo.play.samples);
+ newinfo.monitor_gain =
+ __sparcaudio_if_set_do(drv,
+ drv->ops->set_monitor_volume,
+ drv->ops->get_monitor_volume,
+ ainfo.monitor_gain);
+
+ if (Modify(ainfo.record.buffer_size)) {
+ /* Should sanity check this */
+ newinfo.record.buffer_size = ainfo.record.buffer_size;
+ drv->buffer_size = ainfo.record.buffer_size;
+ } else
+ newinfo.record.buffer_size = drv->buffer_size;
+
+
+ if (Modify(ainfo.play.eof)) {
+ ainfo.play.eof = newinfo.play.eof;
+ newinfo.play.eof = drv->output_eof;
+ drv->output_eof = ainfo.play.eof;
+ } else
+ newinfo.play.eof = drv->output_eof;
+
+ if (drv->flags & SDF_OPEN_READ) {
+ newinfo.record.pause =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_input_pause,
+ drv->ops->get_input_pause,
+ ainfo.record.pause);
+ } else if (drv->ops->get_input_pause) {
+ newinfo.record.pause = drv->ops->get_input_pause(drv);
+ } else newinfo.record.pause = 0;
+
+ if (drv->flags & SDF_OPEN_WRITE) {
+ newinfo.play.pause =
+ __sparcaudio_if_setc_do(drv,
+ drv->ops->set_output_pause,
+ drv->ops->get_output_pause,
+ ainfo.play.pause);
+ } else if (drv->ops->get_output_pause) {
+ newinfo.play.pause = drv->ops->get_output_pause(drv);
+ } else newinfo.play.pause = 0;
+
+ retval = verify_area(VERIFY_WRITE, (void *)arg,
+ sizeof(struct audio_info));
+
+ /* Even if we fail, if we made changes let's try notification */
+ if (!retval)
+ copy_to_user((struct audio_info *)arg, &newinfo,
+ sizeof(newinfo));
+
+#ifdef REAL_AUDIO_SIGNALS
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+#endif
+ break;
}
- /* 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
-
- break;
+
+ default:
+ if (drv->ops->ioctl)
+ retval = drv->ops->ioctl(inode,file,cmd,arg,drv);
+ else
+ retval = -EINVAL;
}
-
+ break;
+ case SPARCAUDIO_STATUS_MINOR:
+ eprintk(("status minor not yet implemented\n"));
+ retval = -EINVAL;
default:
- if (driver->ops->ioctl)
- retval = driver->ops->ioctl(inode,file,cmd,arg,driver);
- else {
- retval = -EINVAL;
- }
+ eprintk(("unknown minor device number\n"));
+ retval = -EINVAL;
}
-
+
return retval;
}
-static int sparcaudioctl_release(struct inode * inode, struct file * file)
+static int sparcaudioctl_release_ret(struct inode * inode, struct file * file)
{
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
- return 0;
+/* For 2.0 kernels */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static void sparcaudioctl_release(struct inode * inode, struct file * file)
+{
+ sparcaudioctl_release_ret(inode, file);
}
+#endif
static struct file_operations sparcaudioctl_fops = {
- NULL,
- NULL,
- NULL,
- NULL, /* sparcaudio_readdir */
- NULL, /* sparcaudio_select */
- sparcaudio_ioctl,
- NULL, /* sparcaudio_mmap */
- NULL,
- NULL, /* flush */
- sparcaudioctl_release
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* sparcaudio_readdir */
+ sparcaudio_select,
+ sparcaudio_ioctl,
+ NULL, /* sparcaudio_mmap */
+ NULL,
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+ NULL, /* sparcaudio_flush */
+#endif
+ sparcaudioctl_release,
};
static int sparcaudio_open(struct inode * inode, struct file * file)
{
- 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);
+ int minor = MINOR(inode->i_rdev);
+ struct sparcaudio_driver *drv =
+ drivers[(minor >> SPARCAUDIO_DEVICE_SHIFT)];
+ int err;
+
+ /* A low-level audio driver must exist. */
+ if (!drv)
+ return -ENODEV;
+
+ switch (minor & 0xf) {
+ 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 && drv->flags & SDF_OPEN_READ) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EBUSY;
+
+ /* If something is now waiting, signal control device */
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+
+ interruptible_sleep_on(&drv->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);
+ return -EINTR;
+ goto retry_open;
+ }
+ if (file->f_mode & FMODE_WRITE && drv->flags & SDF_OPEN_WRITE) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EBUSY;
+
+ /* If something is now waiting, signal control device */
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
+
+ interruptible_sleep_on(&drv->open_wait);
if (signal_pending(current))
- return -EINTR;
- goto retry_open;
- }
+ 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->recording_count = 0;
- driver->ops->start_input(driver, driver->input_buffers[driver->input_front],
- 4096);
- driver->input_active = 1;
- driver->flags |= SDF_OPEN_READ;
+ /* Allow the low-level driver to initialize itself. */
+ if (drv->ops->open) {
+ err = drv->ops->open(inode,file,drv);
+ if (err < 0)
+ return err;
+ }
+
+ /* Mark the driver as locked for read and/or write. */
+ if (file->f_mode & FMODE_READ) {
+ drv->input_offset = 0;
+ drv->input_front = 0;
+ drv->input_rear = 0;
+ drv->input_count = 0;
+ drv->input_size = 0;
+ drv->recording_count = 0;
+ /* Clear pause */
+ if (drv->ops->set_input_pause)
+ drv->ops->set_input_pause(drv, 0);
+ drv->ops->start_input(drv, drv->input_buffers[drv->input_front],
+ drv->input_buffer_size);
+ drv->input_active = 1;
+ drv->flags |= SDF_OPEN_READ;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ drv->output_offset = 0;
+ drv->output_eof = 0;
+ drv->playing_count = 0;
+ drv->output_size = 0;
+ drv->output_front = 0;
+ drv->output_rear = 0;
+ drv->output_count = 0;
+ drv->output_active = 0;
+ /* Clear pause */
+ if (drv->ops->set_output_pause)
+ drv->ops->set_output_pause(drv, 0);
+ drv->flags |= SDF_OPEN_WRITE;
+ }
+
+ break;
+ case SPARCAUDIO_MIXER_MINOR:
+ file->f_op = &sparcaudioctl_fops;
+ break;
+
+ default:
+ return -ENXIO;
}
- if (file->f_mode & FMODE_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;
- }
- break;
- case SPARCAUDIO_MIXER_MINOR:
- file->f_op = &sparcaudioctl_fops;
- break;
-
- default:
- return -ENXIO;
- }
- MOD_INC_USE_COUNT;
-
- /* Success! */
- return 0;
+ MOD_INC_USE_COUNT;
+
+ /* Success! */
+ return 0;
}
-static int sparcaudio_release(struct inode * inode, struct file * file)
+static int sparcaudio_release_ret(struct inode * inode, struct file * file)
{
- /* Anything in the queue? */
- sparcaudio_sync_output(driver);
+ struct sparcaudio_driver *drv = drivers[(MINOR(inode->i_rdev) >>
+ SPARCAUDIO_DEVICE_SHIFT)];
+
+ if (file->f_mode & FMODE_READ) {
+ /* Stop input */
+ drv->ops->stop_input(drv);
+ drv->input_active = 0;
+ }
+
+ if (file->f_mode & FMODE_WRITE) {
+ /* Anything in the queue? */
+ if (drv->output_offset) {
+ drv->output_offset = 0;
+ drv->output_rear = (drv->output_rear + 1)
+ % drv->num_output_buffers;
+ drv->output_count++;
+ }
+ sparcaudio_sync_output(drv);
- /* Stop input */
- driver->ops->stop_input(driver);
- driver->input_active = 0;
+ /* Wait for any output still in the queue to be played. */
+ if ((drv->output_count > 0) || (drv->playing_count > 0))
+ interruptible_sleep_on(&drv->output_drain_wait);
- /* Wait for any output still in the queue to be played. */
- if (driver->output_count > 0)
- interruptible_sleep_on(&driver->output_drain_wait);
+ /* Force any output to be stopped. */
+ drv->ops->stop_output(drv);
+ drv->output_active = 0;
+ drv->playing_count = 0;
+ drv->output_eof = 0;
- /* Force any output to be stopped. */
- driver->ops->stop_output(driver);
- driver->output_active = 0;
+ }
- /* Let the low-level driver do any release processing. */
- if (driver->ops->release)
- driver->ops->release(inode,file,driver);
+ /* Let the low-level driver do any release processing. */
+ if (drv->ops->release)
+ drv->ops->release(inode,file,drv);
- if (file->f_mode & FMODE_READ)
- driver->flags &= ~(SDF_OPEN_READ);
+ if (file->f_mode & FMODE_READ)
+ drv->flags &= ~(SDF_OPEN_READ);
- if (file->f_mode & FMODE_WRITE)
- driver->flags &= ~(SDF_OPEN_WRITE);
+ if (file->f_mode & FMODE_WRITE)
+ drv->flags &= ~(SDF_OPEN_WRITE);
- MOD_DEC_USE_COUNT;
+ /* Status changed. Signal control device */
+ kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
- wake_up_interruptible(&driver->open_wait);
+ MOD_DEC_USE_COUNT;
- return 0;
+ wake_up_interruptible(&drv->open_wait);
+
+ return 0;
+}
+
+/* For 2.0 kernels */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static void sparcaudio_release(struct inode * inode, struct file * file)
+{
+ sparcaudio_release_ret(inode, file);
}
+#endif
static struct file_operations sparcaudio_fops = {
- sparcaudio_llseek,
+ sparcaudio_lseek,
sparcaudio_read,
sparcaudio_write,
NULL, /* sparcaudio_readdir */
- NULL, /* sparcaudio_select */
+ sparcaudio_select,
sparcaudio_ioctl,
NULL, /* sparcaudio_mmap */
sparcaudio_open,
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+ NULL, /* sparcaudio_flush */
+#endif
sparcaudio_release
};
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+static struct symbol_table sparcaudio_syms = {
+#include <linux/symtab_begin.h>
+ X(register_sparcaudio_driver),
+ X(unregister_sparcaudio_driver),
+ X(sparcaudio_output_done),
+ X(sparcaudio_input_done),
+#include <linux/symtab_end.h>
+};
+#else
EXPORT_SYMBOL(register_sparcaudio_driver);
EXPORT_SYMBOL(unregister_sparcaudio_driver);
EXPORT_SYMBOL(sparcaudio_output_done);
EXPORT_SYMBOL(sparcaudio_input_done);
+#endif
#ifdef MODULE
int init_module(void)
@@ -1023,17 +2058,28 @@ int init_module(void)
__initfunc(int sparcaudio_init(void))
#endif
{
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+ /* Export symbols for use by the low-level drivers. */
+ register_symtab(&sparcaudio_syms);
+#endif
+
/* Register our character device driver with the VFS. */
if (register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops))
return -EIO;
+
#ifdef CONFIG_SPARCAUDIO_AMD7930
amd7930_init();
#endif
-
+#ifdef CONFIG_SPARCAUDIO_DBRI
+ dbri_init();
+#endif
#ifdef CONFIG_SPARCAUDIO_CS4231
cs4231_init();
#endif
+#ifdef CONFIG_SPARCAUDIO_DUMMY
+ dummy_init();
+#endif
return 0;
}
@@ -1046,6 +2092,147 @@ void cleanup_module(void)
#endif
/*
+ * Code from Linux Streams, Copyright 1995 by
+ * Graham Wheeler, Francisco J. Ballesteros, Denis Froschauer
+ * and available under GPL
+ */
+
+static int
+lis_add_to_elist( strevent_t **list, pid_t pid, short events )
+{
+ strevent_t *ev = NULL;
+
+ if (*list != NULL)
+ {
+ for (ev=(*list)->se_next;
+ ev != *list && ev->se_pid < pid;
+ ev=ev->se_next
+ );
+ }
+
+ if (ev == NULL || ev == *list) /* no slot for pid in list */
+ {
+ if ((ev = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL))==NULL)
+ return(-ENOMEM);
+
+ if (!*list) /* create dummy head node */
+ {
+ strevent_t *hd;
+ if ((hd = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL)
+ )==NULL)
+ {
+ kfree(ev);
+ return(-ENOMEM);
+ }
+ (*list=hd)->se_pid=0;
+ hd->se_next=hd->se_prev=hd; /* empty list */
+ }
+
+ /* link node last in the list */
+ ev->se_prev=(*list)->se_prev;
+ (*list)->se_prev->se_next=ev;
+ ((*list)->se_prev=ev)->se_next=*list;
+
+ ev->se_pid=pid;
+ ev->se_evs=0;
+ }
+ else if (ev->se_pid!=pid){ /* link node in the middle of the list */
+ strevent_t *new;
+ if ((new = (strevent_t*)kmalloc(sizeof(strevent_t),GFP_KERNEL))==NULL){
+ return(-ENOMEM);
+ }
+ new->se_prev=ev->se_prev;
+ new->se_next=ev;
+ ev->se_prev->se_next=new;
+ ev->se_prev=new;
+ ev = new ; /* use new element */
+ ev->se_pid=pid;
+ ev->se_evs=0;
+ }
+ ev->se_evs|=events;
+ return(0);
+}
+
+static int
+lis_del_from_elist( strevent_t **list, pid_t pid, short events )
+{
+ strevent_t *ev = NULL;
+
+ if (*list != NULL)
+ {
+ for (ev=(*list)->se_next;
+ ev != *list && ev->se_pid < pid;
+ ev=ev->se_next
+ );
+ }
+
+ if (ev == NULL || ev == *list || ev->se_pid != pid )
+ return(1);
+
+ if ( (ev->se_evs &= ~events) == 0 ){ /* unlink */
+ if (ev->se_next) /* should always be true */
+ ev->se_next->se_prev=ev->se_prev;
+ if (ev->se_prev) /* should always be true */
+ ev->se_prev->se_next=ev->se_next;
+ kfree(ev);
+ }
+ return(0);
+}
+
+static void
+lis_free_elist( strevent_t **list )
+{
+ strevent_t *ev;
+ strevent_t *nxt ;
+
+ for (ev = *list; ev != NULL; )
+ {
+ nxt = ev->se_next ;
+ kfree(ev) ;
+ ev = nxt ;
+ if (ev == *list) break ; /* all done */
+ }
+
+ *list = NULL ;
+}
+
+static short
+lis_get_elist_ent( strevent_t *list, pid_t pid )
+{
+ strevent_t *ev = NULL;
+
+ if (list == NULL) return(0) ;
+
+ for(ev = list->se_next ; ev != list && ev->se_pid < pid; ev=ev->se_next )
+ ;
+ if (ev != list && ev->se_pid == pid)
+ return(ev->se_evs);
+ else
+ return(0);
+}
+
+static void
+kill_procs( struct strevent *elist, int sig, short e)
+{
+ strevent_t *ev;
+ int res;
+
+ (void) sig ;
+ if (elist) {
+ for(ev = elist->se_next ; ev != elist; ev=ev->se_next )
+ if ((ev->se_evs & e) != 0){
+ if ((res=kill_proc(ev->se_pid,SIGPOLL,1))<0) {
+ if (res == -3) {
+ lis_del_from_elist(&elist, ev->se_pid, S_ALL);
+ continue;
+ }
+ dprintk(("kill_proc: errno %d\n",res));
+ }
+ }
+ }
+}
+
+/*
* 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
diff --git a/drivers/sbus/audio/cs4215.h b/drivers/sbus/audio/cs4215.h
index ec8de8fad..105dad4da 100644
--- a/drivers/sbus/audio/cs4215.h
+++ b/drivers/sbus/audio/cs4215.h
@@ -41,22 +41,22 @@ static struct {
unsigned char xtal;
unsigned char csval;
} CS4215_FREQ[] = {
- { 8000, 1, (0<<3) },
- { 16000, 1, (1<<3) },
- { 27429, 1, (2<<3) }, /* Actually 24428.57 */
- { 32000, 1, (3<<3) },
- /* { NA, 1, (4<<3) }, */
- /* { NA, 1, (5<<3) }, */
- { 48000, 1, (6<<3) },
- { 9600, 1, (7<<3) },
- { 5513, 2, (0<<3) }, /* Actually 5512.5 */
- { 11025, 2, (1<<3) },
- { 18900, 2, (2<<3) },
- { 22050, 2, (3<<3) },
- { 37800, 2, (4<<3) },
- { 44100, 2, (5<<3) },
- { 33075, 2, (6<<3) },
- { 6615, 2, (7<<3) },
+ { 8000, (1<<4), (0<<3) },
+ { 16000, (1<<4), (1<<3) },
+ { 27429, (1<<4), (2<<3) }, /* Actually 24428.57 */
+ { 32000, (1<<4), (3<<3) },
+ /* { NA, (1<<4), (4<<3) }, */
+ /* { NA, (1<<4), (5<<3) }, */
+ { 48000, (1<<4), (6<<3) },
+ { 9600, (1<<4), (7<<3) },
+ { 5513, (2<<4), (0<<3) }, /* Actually 5512.5 */
+ { 11025, (2<<4), (1<<3) },
+ { 18900, (2<<4), (2<<3) },
+ { 22050, (2<<4), (3<<3) },
+ { 37800, (2<<4), (4<<3) },
+ { 44100, (2<<4), (5<<3) },
+ { 33075, (2<<4), (6<<3) },
+ { 6615, (2<<4), (7<<3) },
{ 0, 0, 0 }
};
#define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */
diff --git a/drivers/sbus/audio/cs4231.c b/drivers/sbus/audio/cs4231.c
index 24a460973..add2737fd 100644
--- a/drivers/sbus/audio/cs4231.c
+++ b/drivers/sbus/audio/cs4231.c
@@ -1,10 +1,10 @@
/*
* drivers/sbus/audio/cs4231.c
*
- * Copyright (C) 1996, 1997 Derrick J Brashear (shadow@andrew.cmu.edu)
+ * Copyright 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
*
* Based on the AMD7930 driver:
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
+ * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
*
* This is the lowlevel driver for the CS4231 audio chip found on some
* sun4m and sun4u machines.
@@ -22,6 +22,8 @@
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/soundcard.h>
+#include <linux/version.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
@@ -35,7 +37,7 @@
#undef __CS4231_DEBUG
#undef __CS4231_TRACE
-#undef __CS4231_ERROR
+#define __CS4231_ERROR
#ifdef __CS4231_ERROR
#define eprintk(x) printk x
#else
@@ -70,7 +72,7 @@ static int cs4231_length_to_samplecount(struct audio_prinfo *thisdir,
static void cs4231_getsamplecount(struct sparcaudio_driver *drv,
unsigned int length, unsigned int value);
-#define CHIP_READY udelay(100); cs4231_ready(drv); mdelay(1);
+#define CHIP_READY udelay(100); cs4231_ready(drv); udelay(1000);
/* Enable cs4231 interrupts atomically. */
static __inline__ void cs4231_enable_interrupts(struct sparcaudio_driver *drv)
@@ -269,7 +271,7 @@ cs4231_set_output_encoding(struct sparcaudio_driver *drv, int value)
return 0;
}
}
- eprintk(("output enc failed\n"));
+ dprintk(("output enc failed\n"));
return -EINVAL;
}
@@ -285,7 +287,7 @@ 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));
+ tprintk(("input encoding %d\n", value));
if (value != 0) {
set_bits = cs4231_encoding_to_bits(drv, value);
if (set_bits >= 0) {
@@ -299,7 +301,7 @@ cs4231_set_input_encoding(struct sparcaudio_driver *drv, int value)
return 0;
}
}
- eprintk(("input enc failed\n"));
+ dprintk(("input enc failed\n"));
return -EINVAL;
}
@@ -329,7 +331,7 @@ cs4231_set_output_rate(struct sparcaudio_driver *drv, int value)
return 0;
}
}
- eprintk(("output rate failed\n"));
+ dprintk(("output rate failed\n"));
return -EINVAL;
}
@@ -345,7 +347,7 @@ 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));
+ tprintk(("input rate %d\n", value));
if (value != 0) {
set_bits = cs4231_rate_to_bits(drv, value);
if (set_bits >= 0) {
@@ -359,7 +361,7 @@ cs4231_set_input_rate(struct sparcaudio_driver *drv, int value)
return 0;
}
}
- eprintk(("input rate failed\n"));
+ dprintk(("input rate failed\n"));
return -EINVAL;
}
@@ -376,7 +378,7 @@ 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));
+ tprintk(("input channels %d\n", value));
cs4231_chip->regs->iar = IAR_AUTOCAL_BEGIN | 0x1c;
tmp_bits = cs4231_chip->regs->idr;
switch (value) {
@@ -387,7 +389,7 @@ cs4231_set_input_channels(struct sparcaudio_driver *drv, int value)
cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
break;
default:
- eprintk(("input chan failed\n"));
+ dprintk(("input chan failed\n"));
return -(EINVAL);
}
@@ -421,7 +423,7 @@ cs4231_set_output_channels(struct sparcaudio_driver *drv, int value)
cs4231_chip->regs->idr = CS4231_STEREO_ON(tmp_bits);
break;
default:
- eprintk(("output chan failed\n"));
+ dprintk(("output chan failed\n"));
return -(EINVAL);
}
@@ -437,29 +439,33 @@ static int cs4231_get_output_channels(struct sparcaudio_driver *drv)
return cs4231_chip->perchip_info.play.channels;
}
-static int cs4231_set_input_precision(struct sparcaudio_driver *drv, int value)
+static int cs4231_get_input_precision(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
- cs4231_chip->perchip_info.record.precision = value;
- return 0;
+ return cs4231_chip->perchip_info.record.precision;
}
-static int cs4231_get_input_precision(struct sparcaudio_driver *drv)
+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.record.precision;
+ return cs4231_chip->perchip_info.play.precision;
}
-static int cs4231_set_output_precision(struct sparcaudio_driver *drv, int value)
+static int cs4231_set_input_precision(struct sparcaudio_driver *drv, int val)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
- cs4231_chip->perchip_info.play.precision = value;
- return 0;
+
+ cs4231_chip->perchip_info.record.precision = val;
+
+ return cs4231_chip->perchip_info.record.precision;
}
-static int cs4231_get_output_precision(struct sparcaudio_driver *drv)
+static int cs4231_set_output_precision(struct sparcaudio_driver *drv, int val)
{
- struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ cs4231_chip->perchip_info.play.precision = val;
+
return cs4231_chip->perchip_info.play.precision;
}
@@ -509,6 +515,12 @@ static int cs4231_get_output_muted(struct sparcaudio_driver *drv)
return cs4231_chip->perchip_info.output_muted;
}
+static int cs4231_get_formats(struct sparcaudio_driver *drv)
+{
+ return (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_IMA_ADPCM |
+ AFMT_S16_LE | AFMT_S16_BE);
+}
+
static int cs4231_get_output_ports(struct sparcaudio_driver *drv)
{
return (AUDIO_LINE_OUT | AUDIO_SPEAKER | AUDIO_HEADPHONE);
@@ -577,11 +589,11 @@ static int cs4231_set_input_port(struct sparcaudio_driver *drv, int value)
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
int retval = 0;
- dprintk(("input port: %d\n", value));
+ tprintk(("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.
+ * More work to be done. CD input mixable, analog loopback may be.
*/
/* Ultra systems do not support AUDIO_INTERNAL_CD_IN */
@@ -663,6 +675,58 @@ static int cs4231_get_monitor_volume(struct sparcaudio_driver *drv)
return (int)cs4231_chip->perchip_info.monitor_gain;
}
+static int cs4231_get_output_error(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ return (int)cs4231_chip->perchip_info.play.error;
+}
+
+static int cs4231_get_input_error(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ return (int)cs4231_chip->perchip_info.record.error;
+}
+
+static int cs4231_get_output_samples(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ int count =
+ cs4231_length_to_samplecount(&cs4231_chip->perchip_info.play,
+ cs4231_chip->regs->dmapc);
+
+ return (cs4231_chip->perchip_info.play.samples -
+ ((count > cs4231_chip->perchip_info.play.samples)
+ ? 0 : count));
+}
+
+static int cs4231_get_input_samples(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ int count =
+ cs4231_length_to_samplecount(&cs4231_chip->perchip_info.record,
+ cs4231_chip->regs->dmacc);
+
+ return (cs4231_chip->perchip_info.record.samples -
+ ((count > cs4231_chip->perchip_info.record.samples) ?
+ 0 : count));
+}
+
+static int cs4231_get_output_pause(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ return (int)cs4231_chip->perchip_info.play.pause;
+}
+
+static int cs4231_get_input_pause(struct sparcaudio_driver *drv)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ return (int)cs4231_chip->perchip_info.record.pause;
+}
+
/* 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)
@@ -704,7 +768,7 @@ 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_record_gain(drv, cs4231_chip->perchip_info.record.gain,
cs4231_chip->perchip_info.record.balance);
return 0;
@@ -745,13 +809,11 @@ static int cs4231_record_gain(struct sparcaudio_driver *drv, int value, unsigned
r = l = value;
if (balance < AUDIO_MID_BALANCE) {
- r = (int)(value - ((AUDIO_MID_BALANCE - balance)
- << AUDIO_BALANCE_SHIFT));
- if (r < 0) r = 0;
+ r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
+ if (r < 0) r = 0;
} else if (balance > AUDIO_MID_BALANCE) {
- l = (int)(value - ((balance - AUDIO_MID_BALANCE)
- << AUDIO_BALANCE_SHIFT));
- if (l < 0) l = 0;
+ l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
+ if (l < 0) l = 0;
}
l_adj = l * (CS4231_MAX_GAIN + 1) / (AUDIO_MAX_GAIN + 1);
@@ -785,20 +847,18 @@ static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned c
tprintk(("in play_gain: %d %c\n", value, balance));
r = l = value;
if (balance < AUDIO_MID_BALANCE) {
- r = (int)(value - ((AUDIO_MID_BALANCE - balance)
- << AUDIO_BALANCE_SHIFT));
- if (r < 0) r = 0;
+ r = (int)(value - ((AUDIO_MID_BALANCE - balance) << AUDIO_BALANCE_SHIFT));
+ if (r < 0) r = 0;
} else if (balance > AUDIO_MID_BALANCE) {
- l = (int)(value - ((balance - AUDIO_MID_BALANCE)
- << AUDIO_BALANCE_SHIFT));
- if (l < 0) l = 0;
+ l = (int)(value - ((balance - AUDIO_MID_BALANCE) << AUDIO_BALANCE_SHIFT));
+ if (l < 0) l = 0;
}
(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) /
+ (r == 0) ? (r_adj = CS4231_MAX_DEV_ATEN) : (r_adj = CS4231_MAX_ATEN -
+ (r * (CS4231_MAX_ATEN + 1) /
(AUDIO_MAX_GAIN + 1)));
cs4231_chip->regs->iar = 0x6;
@@ -811,10 +871,10 @@ static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, unsigned c
if ((value == 0) || (value == AUDIO_MAX_GAIN)) {
tmp = value;
} else {
- if (value == l)
+ if (value == l)
tmp = ((CS4231_MAX_ATEN - l_adj) * (AUDIO_MAX_GAIN + 1) /
(CS4231_MAX_ATEN + 1));
- else if (r == value)
+ else if (value == r)
tmp = ((CS4231_MAX_ATEN - r_adj) * (AUDIO_MAX_GAIN + 1) /
(CS4231_MAX_ATEN + 1));
}
@@ -900,7 +960,16 @@ static void cs4231_chip_reset(struct sparcaudio_driver *drv)
cs4231_output_muted(drv, 0);
cs4231_chip->recording_count = 0;
+ cs4231_chip->input_next_dma_handle = 0;
+ cs4231_chip->input_dma_handle = 0;
+ cs4231_chip->input_next_dma_size = 0;
+ cs4231_chip->input_dma_size = 0;
+
cs4231_chip->playing_count = 0;
+ cs4231_chip->output_next_dma_handle = 0;
+ cs4231_chip->output_dma_handle = 0;
+ cs4231_chip->output_next_dma_size = 0;
+ cs4231_chip->output_dma_size = 0;
}
static int
@@ -945,6 +1014,7 @@ static void cs4231_getsamplecount(struct sparcaudio_driver *drv, unsigned int le
}
count = thisdir->samples;
length = cs4231_length_to_samplecount(thisdir, length);
+ /* normalize for where we are. */
thisdir->samples = ((count - nextcount) + (length - curcount));
}
@@ -957,7 +1027,8 @@ static int cs4231_open(struct inode * inode, struct file * file, struct sparcaud
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);
+ cs4231_chip->perchip_info.play.samples =
+ cs4231_chip->perchip_info.play.error = 0;
}
}
@@ -965,7 +1036,8 @@ static int cs4231_open(struct inode * inode, struct file * file, struct sparcaud
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_chip->perchip_info.record.samples =
+ cs4231_chip->perchip_info.record.error = 0;
}
}
@@ -984,11 +1056,40 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar
/* zero out any info about what data we have as well */
- if (file->f_mode & FMODE_READ)
- cs4231_chip->perchip_info.record.open = 0;
+ if (file->f_mode & FMODE_READ) {
+ /* stop capture here or midlevel? */
+ cs4231_chip->perchip_info.record.open = 0;
+ if (cs4231_chip->input_dma_handle) {
+ mmu_release_scsi_one((u32)((unsigned long)cs4231_chip->input_dma_handle),
+ cs4231_chip->input_dma_size, drv->dev->my_bus);
+ cs4231_chip->input_dma_handle = 0;
+ cs4231_chip->input_dma_size = 0;
+ }
+ if (cs4231_chip->input_next_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
+ cs4231_chip->input_next_dma_size, drv->dev->my_bus);
+ cs4231_chip->input_next_dma_handle = 0;
+ cs4231_chip->input_next_dma_size = 0;
+ }
+ }
- if (file->f_mode & FMODE_WRITE)
+ if (file->f_mode & FMODE_WRITE) {
+ cs4231_chip->perchip_info.play.active =
cs4231_chip->perchip_info.play.open = 0;
+ if (cs4231_chip->output_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
+ cs4231_chip->output_dma_size, drv->dev->my_bus);
+ cs4231_chip->output_dma_handle = 0;
+ cs4231_chip->output_dma_size = 0;
+ }
+ if (cs4231_chip->output_next_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
+ cs4231_chip->output_next_dma_size,
+ drv->dev->my_bus);
+ cs4231_chip->output_next_dma_handle = 0;
+ cs4231_chip->output_next_dma_size = 0;
+ }
+ }
if (!cs4231_chip->perchip_info.play.open &&
!cs4231_chip->perchip_info.record.open &&
@@ -1003,53 +1104,60 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar
static void cs4231_playintr(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ int status = 0;
- if (cs4231_chip->playlen == 0)
+ if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 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;
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
+ cs4231_chip->output_dma_size, drv->dev->my_bus);
+ cs4231_chip->output_dma_handle = 0;
+ cs4231_chip->output_dma_size = 0;
+ cs4231_chip->playing_count--;
+ status++;
}
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;
+ cs4231_chip->output_dma_handle = cs4231_chip->output_next_dma_handle;
+ cs4231_chip->output_dma_size = cs4231_chip->output_next_dma_size;
+ cs4231_chip->output_next_dma_size = 0;
+ 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->playing_count++;
+ if ((cs4231_chip->output_ptr && cs4231_chip->output_size > 0) &&
+ !(cs4231_chip->perchip_info.play.pause)) {
+ cs4231_chip->output_next_dma_handle = (u32 *) (unsigned long)
+ mmu_get_scsi_one((char *) cs4231_chip->output_ptr,
+ cs4231_chip->output_size, drv->dev->my_bus);
+ cs4231_chip->regs->dmapnva = (u32) (unsigned long)
+ cs4231_chip->output_next_dma_handle;
+ cs4231_chip->output_next_dma_size = cs4231_chip->regs->dmapnc =
+ cs4231_chip->output_size;
+ cs4231_chip->output_size = 0;
+ cs4231_chip->output_ptr = NULL;
+ cs4231_chip->playing_count++;
+ status += 2;
+ } else {
+ cs4231_chip->regs->dmapnva = 0;
+ cs4231_chip->regs->dmapnc = 0;
}
- /* 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);
- }
+ sparcaudio_output_done(drv, status);
- return;
+ return;
}
-static void cs4231_recmute(int fmt)
+static void cs4231_recclear(int fmt, char *dmabuf, int length)
{
switch (fmt) {
case AUDIO_ENCODING_LINEAR:
- /* Insert 0x00 from "here" to end of data stream */
+ memset(dmabuf, 0x00, length);
break;
case AUDIO_ENCODING_ALAW:
- /* Insert 0xd5 from "here" to end of data stream */
+ memset(dmabuf, 0xd5, length);
break;
case AUDIO_ENCODING_ULAW:
- /* Insert 0xff from "here" to end of data stream */
+ memset(dmabuf, 0xff, length);
break;
}
}
@@ -1057,19 +1165,51 @@ static void cs4231_recmute(int fmt)
static int cs4231_recintr(struct sparcaudio_driver *drv)
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ int status = 0;
if (cs4231_chip->perchip_info.record.active == 0) {
+ dprintk(("going inactive\n"));
cs4231_pollinput(drv);
- cs4231_recmute(cs4231_chip->perchip_info.record.encoding);
cs4231_disable_rec(drv);
+ }
+
+ if (cs4231_chip->input_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
+ cs4231_chip->input_dma_size, drv->dev->my_bus);
+ cs4231_chip->input_dma_handle = 0;
+ cs4231_chip->input_dma_size = 0;
+ cs4231_chip->recording_count--;
+ status++;
}
- 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;
+ if (cs4231_chip->input_next_dma_handle) {
+ cs4231_chip->input_dma_handle = cs4231_chip->input_next_dma_handle;
+ cs4231_chip->input_dma_size = cs4231_chip->input_next_dma_size;
+ cs4231_chip->input_next_dma_size = 0;
+ cs4231_chip->input_next_dma_handle = 0;
+ }
+
+ if ((cs4231_chip->input_ptr && cs4231_chip->input_size > 0) &&
+ !(cs4231_chip->perchip_info.record.pause)) {
+ cs4231_recclear(cs4231_chip->perchip_info.record.encoding,
+ (char *)cs4231_chip->input_ptr, cs4231_chip->input_size);
+ cs4231_chip->input_next_dma_handle = (u32*) (unsigned long)
+ mmu_get_scsi_one((char *) cs4231_chip->input_ptr,
+ cs4231_chip->input_size, drv->dev->my_bus);
+ cs4231_chip->regs->dmacnva = (u32) (unsigned long)
+ cs4231_chip->input_next_dma_handle;
+ cs4231_chip->input_next_dma_size = cs4231_chip->regs->dmacnc =
+ cs4231_chip->input_size;
cs4231_chip->input_size = 0;
- sparcaudio_input_done(drv);
+ cs4231_chip->input_ptr = NULL;
+ cs4231_chip->recording_count++;
+ status += 2;
+ } else {
+ cs4231_chip->regs->dmacnva = 0;
+ cs4231_chip->regs->dmacnc = 0;
}
+
+ sparcaudio_input_done(drv, status);
+
return 1;
}
@@ -1078,6 +1218,7 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ tprintk(("in 4231 start output\n"));
cs4231_chip->output_ptr = buffer;
cs4231_chip->output_size = count;
@@ -1088,16 +1229,21 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
cs4231_ready(drv);
cs4231_chip->perchip_info.play.active = 1;
-
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;
- cs4231_ready(drv);
+ if ((cs4231_chip->regs->dmacsr & CS_PPAUSE) ||
+ !(cs4231_chip->regs->dmacsr & PDMA_READY)) {
+ cs4231_chip->regs->dmacsr &= ~CS_XINT_PLAY;
+ cs4231_chip->regs->dmacsr &= ~CS_PPAUSE;
+
+ cs4231_playintr(drv);
+
+ cs4231_chip->regs->dmacsr |= CS_PLAY_SETUP;
+ cs4231_enable_play(drv);
+
+ cs4231_ready(drv);
+ } else
+ cs4231_playintr(drv);
}
static void cs4231_stop_output(struct sparcaudio_driver *drv)
@@ -1108,17 +1254,17 @@ static void cs4231_stop_output(struct sparcaudio_driver *drv)
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;
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_dma_handle),
+ cs4231_chip->output_dma_size, drv->dev->my_bus);
+ cs4231_chip->output_dma_handle = 0;
+ cs4231_chip->output_dma_size = 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;
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->output_next_dma_handle),
+ cs4231_chip->output_next_dma_size, drv->dev->my_bus);
+ cs4231_chip->output_next_dma_handle = 0;
+ cs4231_chip->output_next_dma_size = 0;
}
- cs4231_chip->perchip_info.play.active = 0;
- cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
}
static void cs4231_pollinput(struct sparcaudio_driver *drv)
@@ -1137,6 +1283,9 @@ static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer,
{
struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+ cs4231_chip->input_ptr = buffer;
+ cs4231_chip->input_size = count;
+
if (cs4231_chip->perchip_info.record.active ||
(cs4231_chip->perchip_info.record.pause))
return;
@@ -1145,12 +1294,20 @@ static void cs4231_start_input(struct sparcaudio_driver *drv, __u8 * buffer,
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);
+
+ if ((cs4231_chip->regs->dmacsr & CS_CPAUSE) ||
+ !(cs4231_chip->regs->dmacsr & CDMA_READY)) {
+ 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);
+
+ cs4231_ready(drv);
+ } else
+ cs4231_recintr(drv);
}
static void cs4231_stop_input(struct sparcaudio_driver *drv)
@@ -1160,11 +1317,89 @@ static void cs4231_stop_input(struct sparcaudio_driver *drv)
cs4231_chip->perchip_info.record.active = 0;
cs4231_chip->regs->dmacsr |= (CS_CPAUSE);
+ cs4231_chip->input_ptr = NULL;
+ cs4231_chip->input_size = 0;
+ if (cs4231_chip->input_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_dma_handle),
+ cs4231_chip->input_dma_size, drv->dev->my_bus);
+ cs4231_chip->input_dma_handle = 0;
+ cs4231_chip->input_dma_size = 0;
+ }
+ if (cs4231_chip->input_next_dma_handle) {
+ mmu_release_scsi_one((u32) ((unsigned long)cs4231_chip->input_next_dma_handle),
+ cs4231_chip->input_next_dma_size, drv->dev->my_bus);
+ cs4231_chip->input_next_dma_handle = 0;
+ cs4231_chip->input_next_dma_size = 0;
+ }
cs4231_pollinput(drv);
+}
+
+static int cs4231_set_output_pause(struct sparcaudio_driver *drv, int value)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ cs4231_chip->perchip_info.play.pause = value;
+
+ if (!value)
+ sparcaudio_output_done(drv, 0);
- /* need adjust the end pointer, process the input, and clean up the dma */
+ return value;
+}
- cs4231_disable_rec(drv);
+static int cs4231_set_output_error(struct sparcaudio_driver *drv, int value)
+{
+ int i;
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ i = cs4231_chip->perchip_info.play.error;
+ cs4231_chip->perchip_info.play.error = value;
+
+ return i;
+}
+
+static int cs4231_set_input_error(struct sparcaudio_driver *drv, int value)
+{
+ int i;
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ i = cs4231_chip->perchip_info.record.error;
+ cs4231_chip->perchip_info.record.error = value;
+
+ return i;
+}
+
+static int cs4231_set_output_samples(struct sparcaudio_driver *drv, int value)
+{
+ int i;
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ i = cs4231_chip->perchip_info.play.samples;
+ cs4231_chip->perchip_info.play.samples = value;
+
+ return i;
+}
+
+static int cs4231_set_input_samples(struct sparcaudio_driver *drv, int value)
+{
+ int i;
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ i = cs4231_chip->perchip_info.record.samples;
+ cs4231_chip->perchip_info.record.samples = value;
+
+ return i;
+}
+
+static int cs4231_set_input_pause(struct sparcaudio_driver *drv, int value)
+{
+ struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
+
+ cs4231_chip->perchip_info.record.pause = value;
+
+ if (value)
+ cs4231_stop_input(drv);
+
+ return value;
}
static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
@@ -1185,7 +1420,7 @@ static void cs4231_audio_getdev(struct sparcaudio_driver *drv,
static int cs4231_audio_getdev_sunos(struct sparcaudio_driver *drv)
{
- return AUDIO_DEV_CS4231;
+ return AUDIO_DEV_CS4231;
}
static void cs4231_loopback(struct sparcaudio_driver *drv, unsigned int value)
@@ -1237,9 +1472,9 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
if (dummy & CS_XINT_PNVA) {
cs4231_chip->perchip_info.play.samples +=
cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play),
- cs4231_chip->playlen);
+ cs4231_chip->playlen);
cs4231_playintr(drv);
- }
+ }
/* Any other conditions we need worry about? */
}
@@ -1253,16 +1488,25 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/* 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_CEMP) {
+ if (cs4231_chip->perchip_info.record.active == 0) {
+ /* Fix me */
+ cs4231_chip->perchip_info.record.active = 0;
+ cs4231_chip->perchip_info.record.error = 1;
+ cs4231_recintr(drv);
}
- if ((dummy & CS_XINT_EMPT) && (cs4231_chip->perchip_info.play.active == 0)) {
- cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
- cs4231_disable_play(drv);
-
+ }
+
+ if (dummy & CS_XINT_EMPT) {
+ if (!cs4231_chip->output_next_dma_handle) {
+ cs4231_chip->regs->dmacsr |= (CS_PPAUSE);
+ cs4231_disable_play(drv);
+ cs4231_chip->perchip_info.play.error = 1;
+ }
+ cs4231_chip->perchip_info.play.active = 0;
+ cs4231_playintr(drv);
+
cs4231_getsamplecount(drv, cs4231_chip->playlen, 0);
}
@@ -1317,15 +1561,31 @@ static struct sparcaudio_operations cs4231_ops = {
cs4231_get_input_ports,
cs4231_output_muted,
cs4231_get_output_muted,
+ cs4231_set_output_pause,
+ cs4231_get_output_pause,
+ cs4231_set_input_pause,
+ cs4231_get_input_pause,
+ cs4231_set_output_samples,
+ cs4231_get_output_samples,
+ cs4231_set_input_samples,
+ cs4231_get_input_samples,
+ cs4231_set_output_error,
+ cs4231_get_output_error,
+ cs4231_set_input_error,
+ cs4231_get_input_error,
+ cs4231_get_formats,
};
/* Attach to an cs4231 chip given its PROM node. */
static int cs4231_attach(struct sparcaudio_driver *drv,
struct linux_sbus_device *sdev)
{
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+ struct linux_prom_irqs irq;
+#endif
+ struct linux_sbus *sbus = sdev->my_bus;
struct cs4231_chip *cs4231_chip;
int err;
- struct linux_sbus *sbus = sdev->my_bus;
/* Allocate our private information structure. */
drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL);
@@ -1342,8 +1602,15 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
drv->dev = sdev;
/* Map the registers into memory. */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+ prom_apply_sbus_ranges(sbus, &sdev->reg_addrs[0],
+ sdev->num_registers, sdev);
+#else
prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev);
+#endif
+
cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size;
+
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,
@@ -1356,9 +1623,23 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
}
/* Attach the interrupt handler to the audio interrupt. */
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x20100
+ prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq));
+
+ if (irq.pri < 0) {
+ sparc_free_io(cs4231_chip->regs, cs4231_chip->regs_size);
+ kfree(drv->private);
+ return -EIO;
+ }
+
+ cs4231_chip->irq = irq.pri;
+
+#else
cs4231_chip->irq = sdev->irqs[0];
+#endif
request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv);
+
enable_irq(cs4231_chip->irq);
cs4231_enable_interrupts(drv);
@@ -1367,7 +1648,7 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
cs4231_chip_reset(drv);
/* Register ourselves with the midlevel audio driver. */
- err = register_sparcaudio_driver(drv);
+ err = register_sparcaudio_driver(drv, 1);
if (err < 0) {
printk(KERN_ERR "cs4231: unable to register\n");
@@ -1395,8 +1676,8 @@ static int cs4231_attach(struct sparcaudio_driver *drv,
AUDIO_ANALOG_LOOPBACK);
/* Announce the hardware to the user. */
- printk(KERN_INFO "cs4231%c at 0x%lx irq %d\n",
- (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ',
+ printk(KERN_INFO "audio%d: cs4231%c at 0x%lx irq %d\n",
+ drv->index, (cs4231_chip->status & CS_STATUS_REV_A) ? 'a' : ' ',
(unsigned long)cs4231_chip->regs, cs4231_chip->irq);
/* Success! */
@@ -1438,7 +1719,7 @@ 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);
+ unregister_sparcaudio_driver(drv, 1);
disable_irq(info->irq);
free_irq(info->irq, drv);
sparc_free_io(info->regs, info->regs_size);
diff --git a/drivers/sbus/audio/cs4231.h b/drivers/sbus/audio/cs4231.h
index 569950062..31dc849c0 100644
--- a/drivers/sbus/audio/cs4231.h
+++ b/drivers/sbus/audio/cs4231.h
@@ -48,11 +48,14 @@ struct cs4231_chip {
/* 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;
+ volatile __u32 * output_dma_handle, * output_next_dma_handle;
+ volatile unsigned long output_dma_size, output_next_dma_size;
/* Current record buffer. */
volatile __u8 * input_ptr;
volatile unsigned long input_size;
+ volatile __u32 * input_dma_handle, * input_next_dma_handle;
+ volatile unsigned long input_dma_size, input_next_dma_size;
/* Number of buffers in the pipe. */
volatile unsigned long playing_count;
diff --git a/drivers/sbus/audio/dbri.c b/drivers/sbus/audio/dbri.c
index 0456282ef..c943aacd4 100644
--- a/drivers/sbus/audio/dbri.c
+++ b/drivers/sbus/audio/dbri.c
@@ -51,6 +51,7 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/malloc.h>
+#include <linux/version.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
@@ -59,12 +60,16 @@
#include <asm/delay.h>
#include <asm/sbus.h>
-#include "audio.h"
+#include <asm/audioio.h>
#include "dbri.h"
+#if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+#include "../../isdn/hisax/hisax.h"
+#include "../../isdn/hisax/isdnl1.h"
+#include "../../isdn/hisax/foreign.h"
+#endif
-
-#define DBRI_DEBUG
+/* #define DBRI_DEBUG */
#ifdef DBRI_DEBUG
@@ -84,10 +89,13 @@ static char *cmds[] = {
/* Bit hunting */
#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->cmd[i]); }
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
+
#else
#define dprintk(a, x)
#define dumpcmd
+#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
#endif /* DBRI_DEBUG */
@@ -101,50 +109,60 @@ static char *cmds[] = {
static struct sparcaudio_driver drivers[MAX_DRIVERS];
static char drv_name[] = "DBRI/audio";
static int num_drivers;
-static int dbri_cmdlocked = 0;
+static void * output_callback_arg;
/*
- * Make sure, that we can send a command to the dbri
+ * Commands are sent to the DBRI by building a list of them in memory,
+ * then writing the address of the first list item to DBRI register 8.
+ * The list is terminated with a WAIT command, which can generate a
+ * CPU interrupt if required.
+ *
+ * Since the DBRI can run asynchronously to the CPU, several means of
+ * synchronization present themselves. The original scheme (Rudolf's)
+ * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+ * an interrupt signaled completion, and wait on a wait_queue if a routine
+ * attempted to cmdlock while the flag was set. The problems arose when
+ * we tried to cmdlock from inside an interrupt handler, which might
+ * cause scheduling in an interrupt (if we waited), etc, etc
+ *
+ * A more sophisticated scheme might involve a circular command buffer
+ * or an array of command buffers. A routine could fill one with
+ * commands and link it onto a list. When a interrupt signaled
+ * completion of the current command buffer, look on the list for
+ * the next one.
+ *
+ * I've decided to implement something much simpler - after each command,
+ * the CPU waits for the DBRI to finish the command by polling the P bit
+ * in DBRI register 0. I've tried to implement this in such a way
+ * that might make implementing a more sophisticated scheme easier.
+ *
+ * Every time a routine wants to write commands to the DBRI, it
+ * must first call dbri_cmdlock() and get an initial index into dbri->cmd
+ * (currently always 0) in return. After the commands have been
+ * write (index incremented after each one), dbri_cmdsend() is called
+ * with the final index value.
*/
+
static int dbri_cmdlock(struct dbri *dbri)
{
- unsigned long flags;
- int was_sleeping = 0;
-
- save_flags(flags);
- cli();
+ return 0;
+}
- if(dbri_cmdlocked) {
- interruptible_sleep_on(&dbri->wait);
- was_sleeping = 1;
- }
- if(dbri_cmdlocked)
- return -EINTR;
- dbri_cmdlocked = 1;
+static void dbri_cmdsend(struct dbri *dbri, int n)
+{
+ int maxloops = 1000000;
- restore_flags(flags);
+ dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
+ dbri->regs->reg8 = (int)dbri->cmd;
- if(was_sleeping)
- dprintk(D_INT, ("DBRI: Just woke up\n"));
- return 0;
-}
+ while (maxloops > 0 && (dbri->regs->reg0 & D_P));
-static void dummy()
-{
+ if (maxloops == 0) {
+ printk("DBRI: Maxloops exceeded in dbri_cmdsend\n");
+ }
}
-static struct sparcaudio_operations dbri_ops = {
- dummy, /* dbri_open, */
- dummy, /* dbri_release, */
- dummy, /* dbri_ioctl, */
- dummy, /* dbri_start_output, */
- dummy, /* dbri_stop_output, */
- dummy, /* dbri_start_input, */
- dummy, /* dbri_stop_input, */
- dummy, /* dbri_audio_getdev, */
-};
-
static void dbri_reset(struct sparcaudio_driver *drv)
{
struct dbri *dbri = (struct dbri *)drv->private;
@@ -164,14 +182,14 @@ static void dbri_detach(struct sparcaudio_driver *drv)
struct dbri *info = (struct dbri *)drv->private;
dbri_reset(drv);
- unregister_sparcaudio_driver(drv);
+ unregister_sparcaudio_driver(drv, 1);
free_irq(info->irq, drv);
sparc_free_io(info->regs, info->regs_size);
kfree(drv->private);
}
-static void dbri_init(struct sparcaudio_driver *drv)
+static void dbri_initialize(struct sparcaudio_driver *drv)
{
struct dbri *dbri = (struct dbri *)drv->private;
int n;
@@ -191,18 +209,23 @@ static void dbri_init(struct sparcaudio_driver *drv)
dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr);
dbri->dbri_irqp = 1;
+#ifdef USE_SBUS_BURSTS
+ /* Enable 4-word, 8-word, and 16-word SBus Bursts */
dbri->regs->reg0 |= (D_G|D_S|D_E);
+#else
+ /* Disable 4-word, 8-word, and 16-word SBus Bursts */
+ dbri->regs->reg0 &= ~(D_G|D_S|D_E);
+#endif
/*
* Set up the interrupt queue
*/
- (void)dbri_cmdlock(dbri);
+ n = dbri_cmdlock(dbri);
- n = 0;
dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0);
dbri->cmd[n++] = (int)(dbri->intr);
- dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1);
- dbri->regs->reg8 = (int)dbri->cmd;
+
+ dbri_cmdsend(dbri, n);
}
@@ -236,7 +259,7 @@ static void mmcodec_default(struct cs4215 *mm)
*/
mm->data[0] = CS4215_LO(0x20) | CS4215_HE|CS4215_LE;
mm->data[1] = CS4215_RO(0x20) | CS4215_SE;
- mm->data[2] = CS4215_LG( 0x8) | CS4215_IS;
+ mm->data[2] = CS4215_LG( 0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1;
mm->data[3] = CS4215_RG( 0x8) | CS4215_MA(0xf);
/*
@@ -248,89 +271,104 @@ static void mmcodec_default(struct cs4215 *mm)
*/
mm->ctrl[0] = CS4215_RSRVD_1;
mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
- mm->ctrl[2] = CS4215_XEN | CS4215_XCLK |
+ mm->ctrl[2] = CS4215_XCLK |
CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
mm->ctrl[3] = 0;
}
static void mmcodec_init_data(struct dbri *dbri)
{
- int val, n = 0;
+ int val, n;
- dbri_cmdlock(dbri);
+ n = dbri_cmdlock(dbri);
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
* Pipe 17: Send timeslots 5-8 (part of ctrl data)
* Pipe 6: Receive timeslots 1-4 (audio data)
- * Pipe 19: Receive timeslots 6-7. We can only receive 20 bits via
+ * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via
* interrupt, and the rest of the data (slot 5 and 8) is
* not relevant for us (only for doublechecking).
+ *
+ * Just like in control mode, the time slots are all offset by eight
+ * bits. The CS4215, it seems, observes TSIN (the delayed signal)
+ * even if it's the CHI master. Don't ask me...
*/
- /* Transmit & Receive Memory setup */
- dbri->mm.td.flags = DBRI_TD_F|DBRI_TD_D|DBRI_TD_CNT(0);
- dbri->mm.td.ba = 0;
- dbri->mm.td.nda = (__u32)&dbri->mm.td;
- dbri->mm.td.status = 0;
-
- dbri->mm.td.flags = DBRI_RD_BCNT(0);
- dbri->mm.td.ba = 0;
- dbri->mm.td.nda = (__u32)&dbri->mm.rd;
- dbri->mm.td.status = 0;
-
- /* Pipe 4: SDP + DTS */
- val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_4);
- dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
- dbri->cmd[n++] = (__u32)&dbri->mm.td;
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
- dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ /* Pipe 4: SDP */
+ val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
- dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
- /* Pipe 17: SDP + DTS + SSP */
+ /* Pipe 17: SDP */
val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0; /* Fixed data */
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
- dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
- dbri->cmd[n++] = 0;
- dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(32) | D_TS_NONCONTIG |
- D_TS_MON(D_P_4) | D_TS_NEXT(D_P_16);
-
+ /* Pipe 17: SSP */
dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4);
- /* Pipe 6: SDP + DTS */
- val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_6);
+ /* Pipe 6: SDP */
+ val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_6);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
- dbri->cmd[n++] = (__u32)&dbri->mm.rd;
-
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
- dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
- dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16);
dbri->cmd[n++] = 0;
- /* Pipe 19: SDP + DTS */
- val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+ /* Pipe 20: SDP */
+ val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_20);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0; /* Fixed data */
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_19);
+
+
+ dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+
+
+ /* Pipe 4: DTS */
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
- dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(40) | D_TS_NONCONTIG |
- D_TS_MON(D_P_6) | D_TS_NEXT(D_P_16);
dbri->cmd[n++] = 0;
+#if 0
+ /* Full blown, four time slots, 16 bit stereo */
+ dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#else
+ /* Single time slot, 8 bit mono */
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#endif
- dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1);
+ /* Pipe 17: DTS */
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = 0;
+ dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(40) | D_TS_NEXT(D_P_16);
- dbri->regs->reg8 = (int)dbri->cmd;
+ /* Pipe 6: DTS */
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+#if 0
+ /* Full blown, four time slots, 16 bit stereo */
+ dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#else
+ /* Single time slot, 8 bit mono */
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_16);
+#endif
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 20: DTS */
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_20);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16);
+ dbri->cmd[n++] = 0;
+
+ /* CHI: Slave mode; enable interrupts */
+ dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN);
+
+ dbri_cmdsend(dbri, n);
}
@@ -339,17 +377,36 @@ static void mmcodec_init_data(struct dbri *dbri)
*/
static void mmcodec_setctrl(struct dbri *dbri)
{
- int n = 0, val;
+ int n, val;
/*
- * Enable Command mode: Set PIO3 to 0, then wait
+ * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
* 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
*/
val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2);
dbri->regs->reg2 = val;
- udelay(34);
-
- dbri_cmdlock(dbri);
+ udelay(34);
+
+ /* In Control mode, the CS4215 is a slave device, so the DBRI must
+ * operate as CHI master, supplying clocking and frame synchronization.
+ *
+ * In Data mode, however, the CS4215 must be CHI master to insure
+ * that its data stream is synchronous with its codec.
+ *
+ * The upshot of all this? We start by putting the DBRI into master
+ * mode, program the CS4215 in Control mode, then switch the CS4215
+ * into Data mode and put the DBRI into slave mode. Various timing
+ * requirements must be observed along the way.
+ *
+ * Oh, and one more thing - when the DBRI is master (and only when
+ * the DBRI is master), the addressing of the CS4215's time slots
+ * is offset by eight bits, so we add eight to all the "cycle"
+ * values in the Define Time Slot (DTS) commands. This is done in
+ * hardware by a TI 248 that delays the DBRI->4215 frame sync signal
+ * by eight clock cycles. Anybody know why?
+ */
+
+ n = dbri_cmdlock(dbri);
/*
* Control mode:
@@ -371,11 +428,11 @@ static void mmcodec_setctrl(struct dbri *dbri)
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
- val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_18);
+ val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_18);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
- val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_19);
+ val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_C|D_PIPE(D_P_19);
dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
dbri->cmd[n++] = 0;
@@ -389,76 +446,88 @@ static void mmcodec_setctrl(struct dbri *dbri)
/* Link the timeslots */
+
+ /* Pipe 17 - CS4215 Status, Data Format, Serial Control, Test - output
+ * time slots 1, 2, 3 and 4 - 32 bits
+ */
+
val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = 0;
- dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(256) | D_TS_NEXT(D_P_16);
+ dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
+
+ /* Pipe 18 - CS4215 Status and Data Format - input
+ * time slots 1 & 2 - 16 bits
+ */
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
- dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(0) | D_TS_NEXT(D_P_16);
+ dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(8) | D_TS_NEXT(D_P_16);
dbri->cmd[n++] = 0;
+ /* Pipe 19 - CS4215 Revision - time slot 7, eight bits - input
+ */
+
val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
- dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16);
- /*
- * According to the manual we should also specify
- * D_TS_NONCONTIG | D_TS_MON(D_P_18), but the machine freezes
- * if we do that. Can somebody explain me why?
- */
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(56) | D_TS_NEXT(D_P_16);
dbri->cmd[n++] = 0;
- /* Setup DBRI for CHI Master */
- dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_REN);
- dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(6) | D_CHI_FD |
- D_CHI_IR | D_CHI_EN);
- dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+ /* Setup DBRI for CHI Master
+ *
+ * BPF = 128 (128 bits per 8 kHz frame = 1.024 MHz clock rate)
+ * CHICM = 12 (12.288 MHz / 24 = 1.024 MHz clock rate)
+ * FD = 1 - drive CHIFS on rising edge of CHICK
+ *
+ * RCE = 0 - receive on falling edge of CHICK
+ * XCE = 1 - transmit on rising edge of CHICK
+ */
+ dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(12) | D_CHI_FD |
+ D_CHI_IR | D_CHI_EN | D_CHI_BPF(128));
dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+ dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+
+ /* Wait for the command to complete */
+ dbri_cmdsend(dbri, n);
+
+ /* Switch CS4215 to data mode - data sheet says
+ * "Set CLB=1 and send two more frames of valid control info"
+ */
+ n = dbri_cmdlock(dbri);
+
+ dbri->mm.ctrl[0] |= CS4215_CLB;
+ dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17));
+ dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
- dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0);
- dbri->regs->reg8 = (int)dbri->cmd;
+ dbri_cmdsend(dbri, n);
- /* Wait for the data from the CS4215 */
- interruptible_sleep_on(&dbri->int_wait);
-printk("Woke up (1) reg2: %x\n", dbri->regs->reg2);
+ /* Two frames of control info @ 8kHz frame rate = 250 us delay */
+ udelay(250);
+ n = dbri_cmdlock(dbri);
/* Now switch back to data mode */
- n = 0;
- /* CHI Anchor: Stop Send/Receive */
+ /* Reset CHI Anchor: Stop Send/Receive */
val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
- dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0x17);
- dbri->regs->reg8 = (int)dbri->cmd;
-
-#if 0
- dbri->mm.ctrl[0] |= CS4215_CLB;
- dbri->cmd[n++] = DBRI_CMD(D_SSP, 1, D_PIPE(D_P_17));
- dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4);
/* Setup DBRI for CHI Slave */
- dbri->cmd[n++] = DBRI_CMD(D_CDM, 1, D_CDM_XCE|D_CDM_REN);
- dbri->cmd[n++] = DBRI_CMD(D_CHI, 1, D_CHI_CHICM(1) | D_CHI_FD |
- D_CHI_IR | D_CHI_EN);
- dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 1, 0x16);
- dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+ dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+ /* dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) | D_CHI_IR | D_CHI_EN); */
+ dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0x16);
- dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, 0x17);
- dbri->regs->reg8 = (int)dbri->cmd;
- dbri->regs->reg2 = D_ENPIO | D_PIO3 |
- (dbri->mm.onboard ? D_PIO0 : D_PIO2);
-#endif
+ /* Wait for command to complete */
+ dbri_cmdsend(dbri, n);
- /* We are ready */
- dbri_cmdlocked = 0;
- wake_up(&dbri->wait);
+ /* Switch CS4215 to data mode - set PIO3 to 1 */
+ dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
+ (dbri->mm.onboard ? D_PIO0 : D_PIO2);
}
static int mmcodec_init(struct sparcaudio_driver *drv)
@@ -500,13 +569,99 @@ static int mmcodec_init(struct sparcaudio_driver *drv)
if(dbri->mm.version == 0xff)
return -EIO;
- /*
- mmcodec_init_data(dbri, &n);
- */
+ mmcodec_init_data(dbri);
return 0;
}
+void dbri_isdn_init(struct dbri *dbri)
+{
+ int n, val;
+
+ /* Pipe 0: Receive D channel
+ * Pipe 8: Receive B1 channel
+ * Pipe 9: Receive B2 channel
+ * Pipe 1: Transmit D channel
+ * Pipe 10: Transmit B1 channel
+ * Pipe 11: Transmit B2 channel
+ */
+
+ n = dbri_cmdlock(dbri);
+
+ /* Pipe 0: SDP */
+ val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 8: SDP */
+ val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 9: SDP */
+ val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_9);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 1: SDP */
+ val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 10: SDP */
+ val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 11: SDP */
+ val = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_11);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+
+ dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0);
+
+ /* Pipe 0: DTS */
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_0);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_0);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 8: DTS */
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_0) | D_PIPE(D_P_8);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_0);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 9: DTS */
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_8) | D_PIPE(D_P_9);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_0);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 1: DTS */
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_1);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = 0;
+ dbri->cmd[n++] = D_TS_LEN(2) | D_TS_CYCLE(17)| D_TS_NEXT(D_P_1);
+
+ /* Pipe 10: DTS */
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_1) | D_PIPE(D_P_10);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = 0;
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_1);
+
+ /* Pipe 11: DTS */
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_10) | D_PIPE(D_P_11);
+ dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val);
+ dbri->cmd[n++] = 0;
+ dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(8)| D_TS_NEXT(D_P_1);
+
+
+ /* Wait for command to complete */
+ dbri_cmdsend(dbri, n);
+}
+
void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id;
@@ -518,11 +673,13 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
* Read it, so the interrupt goes away.
*/
x = dbri->regs->reg1;
+#if 0
if(numint++ > 20) {
dbri->regs->reg0 = D_R; /* Soft Reset */
numint = 0;
printk("Soft reset\n");
}
+#endif
if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
/*
@@ -552,8 +709,16 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
val = D_INTR_GETVAL(x);
+ if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
+ int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
+ dbri->liu_state = liu_states[val & 0x7];
+ if (dbri->liu_callback)
+ dbri->liu_callback(dbri->liu_callback_arg);
+ }
+
switch(D_INTR_GETCHAN(x)) {
case D_INTR_CMD:
+#if 0
if(D_INTR_GETCMD(x) == D_WAIT)
if(val == WAIT_INTR1) {
dbri_cmdlocked = 0;
@@ -561,19 +726,86 @@ void dbri_intr(int irq, void *dev_id, struct pt_regs *regs)
}
if(val == WAIT_INTR2)
wake_up(&dbri->int_wait);
+#endif
+ break;
+
+ case D_P_0:
+ /* Pipe 0 - D channel receive */
+ if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
+ dbri->D.input_callback) {
+ dbri->D.input_callback(dbri->D.input_callback_arg,
+ DBRI_RD_STATUS(dbri->D.rd.flags),
+ DBRI_RD_CNT(dbri->D.rd.flags)-2);
+ }
+ break;
+
+ case D_P_1:
+ /* Pipe 1 - D channel transmit */
+ if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
+ dbri->D.output_callback) {
+ dbri->D.output_callback(dbri->D.output_callback_arg,
+ DBRI_TD_STATUS(dbri->D.rd.flags)&0xe);
+ }
+ break;
+
+ case D_P_4:
+ /* Pipe 4 - audio transmit */
+ if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
+ sparcaudio_output_done(output_callback_arg, 1);
+ }
break;
+
+ case D_P_8:
+ /* Pipe 8 - B1 channel receive */
+ if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
+ dbri->B[0].input_callback) {
+ dbri->B[0].input_callback(dbri->B[0].input_callback_arg,
+ DBRI_RD_STATUS(dbri->B[0].rd.flags),
+ DBRI_RD_CNT(dbri->B[0].rd.flags)-2);
+ }
+ break;
+
+ case D_P_9:
+ /* Pipe 9 - B2 channel receive */
+ if (D_INTR_GETCODE(x) == D_INTR_BRDY &&
+ dbri->B[1].input_callback) {
+ dbri->B[1].input_callback(dbri->B[1].input_callback_arg,
+ DBRI_RD_STATUS(dbri->B[1].rd.flags),
+ DBRI_RD_CNT(dbri->B[1].rd.flags)-2);
+ }
+ break;
+
+ case D_P_10:
+ /* Pipe 10 - B1 channel transmit */
+ if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
+ dbri->B[0].output_callback) {
+ dbri->B[0].output_callback(dbri->B[0].output_callback_arg,
+ DBRI_TD_STATUS(dbri->B[0].rd.flags)&0xfe);
+ }
+ break;
+
+ case D_P_11:
+ /* Pipe 11 - B2 channel transmit */
+ if (D_INTR_GETCODE(x) == D_INTR_XCMP &&
+ dbri->B[1].output_callback) {
+ dbri->B[1].output_callback(dbri->B[1].output_callback_arg,
+ DBRI_TD_STATUS(dbri->B[1].rd.flags)&0xfe);
+ }
+ break;
+
case D_P_18:
+ /* Pipe 18 - receive CS4215 status */
if(val != 0) {
x = reverse_bytes(val,2)&CS4215_12_MASK;
-printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
- if(x == (*(int *)dbri->mm.ctrl >> 16))
-{
-printk("Comp ok\n");
- wake_up(&dbri->int_wait);
-}
+ printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl);
+ if(x == (*(int *)dbri->mm.ctrl >> 16)) {
+ printk("Comp ok\n");
+ wake_up(&dbri->int_wait);
+ }
}
break;
case D_P_19:
+ /* Pipe 19 - receive CS4215 version */
if(val != 0) {
dbri->mm.version =
reverse_bytes(val, 1) & 0xf;
@@ -591,8 +823,448 @@ printk("Comp ok\n");
}
+/*
+****************************************************************************
+******************** Interface with sparcaudio midlevel ********************
+****************************************************************************
+*/
+static void dummy()
+{
+}
+
+static int dbri_open(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static void dbri_release(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+static void dbri_start_output(struct sparcaudio_driver *drv,
+ __u8 * buffer, unsigned long count)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int val, n;
+
+ if (count > (1 << 14) - 1) {
+ printk("dbri_start_output called with count=%d; truncated", count);
+ count = (1 << 14) - 1;
+ }
+
+ n = dbri_cmdlock(dbri);
+
+ dbri->mm.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_D | DBRI_TD_CNT(count);
+ dbri->mm.td.ba = (__u32) buffer;
+ dbri->mm.td.nda = 0;
+ dbri->mm.td.status = 0;
+
+ /* Pipe 4 is audio transmit */
+ val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_MSB|D_PIPE(D_P_4);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = (__u32)&dbri->mm.td;
+
+ output_callback_arg = drv;
+
+ dbri_cmdsend(dbri, n);
+}
+
+static void dbri_stop_output(struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+}
+
+
+
+static struct sparcaudio_operations dbri_ops = {
+ dbri_open,
+ dbri_release,
+ dummy, /* dbri_ioctl, */
+ dbri_start_output,
+ dbri_stop_output,
+ dummy, /* dbri_start_input, */
+ dummy, /* dbri_stop_input, */
+ dummy, /* dbri_audio_getdev, */
+ dummy, /* dbri_set_output_volume, */
+ dummy, /* dbri_get_output_volume, */
+ dummy, /* dbri_set_input_volume, */
+ dummy, /* dbri_get_input_volume, */
+ dummy, /* dbri_set_monitor_volume, */
+ dummy, /* dbri_get_monitor_volume, */
+ dummy, /* dbri_set_output_balance */
+ dummy, /* dbri_get_output_balance, */
+ dummy, /* dbri_set_input_balance */
+ dummy, /* dbri_get_input_balance, */
+ dummy, /* dbri_set_output_channels */
+ dummy, /* dbri_get_output_channels, */
+ dummy, /* dbri_set_input_channels */
+ dummy, /* dbri_get_input_channels, */
+ dummy, /* dbri_set_output_precision */
+ dummy, /* dbri_get_output_precision, */
+ dummy, /* dbri_set_input_precision */
+ dummy, /* dbri_get_input_precision, */
+ dummy, /* dbri_set_output_port */
+ dummy, /* dbri_get_output_port, */
+ dummy, /* dbri_set_input_port */
+ dummy, /* dbri_get_input_port, */
+ dummy, /* dbri_set_output_encoding */
+ dummy, /* dbri_get_output_encoding, */
+ dummy, /* dbri_set_input_encoding */
+ dummy, /* dbri_get_input_encoding, */
+ dummy, /* dbri_set_output_rate */
+ dummy, /* dbri_get_output_rate, */
+ dummy, /* dbri_set_input_rate */
+ dummy, /* dbri_get_input_rate, */
+ dummy, /* dbri_sunaudio_getdev_sunos, */
+ dummy, /* dbri_get_output_ports, */
+ dummy, /* dbri_get_input_ports, */
+ dummy, /* dbri_set_output_muted */
+ dummy, /* dbri_get_output_muted, */
+};
+
+/*
+****************************************************************************
+************************** ISDN (Hisax) Interface **************************
+****************************************************************************
+*/
+
+int dbri_get_irqnum(int dev)
+{
+ struct dbri *dbri;
+
+ if (dev > num_drivers) {
+ return(0);
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ /* On the sparc, the cpu's irq number is only part of the "irq" */
+ return (dbri->irq & NR_IRQS);
+}
+
+int dbri_get_liu_state(int dev)
+{
+ struct dbri *dbri;
+
+ if (dev > num_drivers) {
+ return(0);
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ return dbri->liu_state;
+}
+
+void dbri_liu_init(int dev, void (*callback)(void *), void *callback_arg)
+{
+ struct dbri *dbri;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ /* Set callback for LIU state change */
+ dbri->liu_callback = callback;
+ dbri->liu_callback_arg = callback_arg;
+
+ dbri_isdn_init(dbri);
+}
+
+void dbri_liu_activate(int dev, int priority)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ n = dbri_cmdlock(dbri);
+
+ /* Turn on the ISDN TE interface and request activation */
+ val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
+ dbri->cmd[n++] = DBRI_CMD(D_TE, 0, val);
+
+ dbri_cmdsend(dbri, n);
+
+ /* Activate the interface */
+ dbri->regs->reg0 |= D_T;
+}
+
+void dbri_liu_deactivate(int dev)
+{
+ struct dbri *dbri;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ /* Turn off the ISDN TE interface */
+ dbri->regs->reg0 &= ~D_T;
+}
+
+void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
+ void (*callback)(void *, int), void *callback_arg)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ if (count > (1 << 14) - 1) {
+ printk("dbri_dxmit called with count=%d; truncated", count);
+ count = (1 << 14) - 1;
+ }
+
+ n = dbri_cmdlock(dbri);
+
+ /* XXX - Shouldn't I check to make sure D.td isn't is use? */
+
+ dbri->D.td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count) | DBRI_TD_I;
+ dbri->D.td.ba = (__u32) buffer;
+ dbri->D.td.nda = 0;
+ dbri->D.td.status = 0;
+
+ /* Pipe 1 is D channel transmit */
+ val = D_SDP_HDLC_D|D_SDP_TO_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_1);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = (__u32)&dbri->D.td;
+
+ dbri->D.output_callback = callback;
+ dbri->D.output_callback_arg = callback_arg;
+
+ dbri_cmdsend(dbri, n);
+}
+
+void dbri_drecv(int dev, __u8 *buffer, unsigned int size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ if (size > (1 << 14) - 1) {
+ printk("dbri_drecv called with size=%d; truncated", size);
+ size = (1 << 14) - 1;
+ }
+
+ /* Make sure size is a multiple of four */
+ size &= ~3;
+
+ n = dbri_cmdlock(dbri);
+
+ /* XXX - Shouldn't I check to make sure D.rd isn't is use? */
+
+ dbri->D.rd.flags = 0;
+ dbri->D.rd.ba = (__u32) buffer;
+ dbri->D.rd.nda = 0;
+ dbri->D.rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
+
+ /* Pipe 0 is D channel receive */
+ val = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_C|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_0);
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = (__u32)&dbri->D.rd;
+
+ dbri_cmdsend(dbri, n);
+
+ dbri->D.input_callback = callback;
+ dbri->D.input_callback_arg = callback_arg;
+}
+
+int dbri_bopen(int dev, unsigned int chan,
+ int hdlcmode, u_char xmit_idle_char)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers || chan > 1) {
+ return -1;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ if (hdlcmode) {
+
+ return -1;
+
+ /* Pipe 8/9: receive B1/B2 channel */
+ dbri->B[chan].recvSDP = D_SDP_HDLC|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan);
+
+ /* Pipe 10/11: transmit B1/B2 channel */
+ dbri->B[chan].xmitSDP = D_SDP_HDLC|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan);
+
+ } else { /* !hdlcmode means transparent */
+
+ /* Pipe 8/9: receive B1/B2 channel */
+ dbri->B[chan].recvSDP = D_SDP_MEM|D_SDP_FROM_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_8+chan);
+
+ /* Pipe 10/11: transmit B1/B2 channel */
+ dbri->B[chan].xmitSDP = D_SDP_MEM|D_SDP_TO_SER|D_SDP_P|D_SDP_LSB|D_PIPE(D_P_10+chan);
+
+ }
+
+ n = dbri_cmdlock(dbri);
+
+ /* Pipe 8/9: receive B1/B2 channel */
+ val = dbri->B[chan].recvSDP | D_SDP_C;
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ /* Pipe 10/11: transmit B1/B2 channel */
+ val = dbri->B[chan].xmitSDP | D_SDP_C;
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = 0;
+
+ dbri->B[chan].output_callback = NULL;
+ dbri->B[chan].input_callback = NULL;
+
+ dbri_cmdsend(dbri, n);
+
+ return 0;
+}
+
+void dbri_bclose(int dev, unsigned int chan)
+{
+ struct dbri *dbri;
+
+ if (dev > num_drivers || chan > 1) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ dbri->B[chan].output_callback = NULL;
+ dbri->B[chan].input_callback = NULL;
+}
+
+void dbri_bxmit(int dev, unsigned int chan,
+ __u8 *buffer, unsigned long count,
+ void (*callback)(void *, int),
+ void *callback_arg)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers || chan > 1) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ if (count > (1 << 14) - 1) {
+ printk("dbri_bxmit called with count=%ld; truncated", count);
+ count = (1 << 14) - 1;
+ }
+
+ n = dbri_cmdlock(dbri);
+
+ /* XXX - Shouldn't I check to make sure td isn't is use? */
+
+ dbri->B[chan].td.flags = DBRI_TD_F | DBRI_TD_B | DBRI_TD_CNT(count);
+ dbri->B[chan].td.ba = (__u32) buffer;
+ dbri->B[chan].td.nda = 0;
+ dbri->B[chan].td.status = 0;
+
+ /* Pipe 10/11 is B1/B2 channel transmit */
+ val = dbri->B[chan].xmitSDP;
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = (__u32)&dbri->B[chan].td;
+
+ dbri->B[chan].output_callback = callback;
+ dbri->B[chan].output_callback_arg = callback_arg;
+
+ dbri_cmdsend(dbri, n);
+}
+
+void dbri_brecv(int dev, unsigned int chan,
+ __u8 *buffer, unsigned long size,
+ void (*callback)(void *, int, unsigned int),
+ void *callback_arg)
+{
+ struct dbri *dbri;
+ int n, val;
+
+ if (dev > num_drivers || chan > 1) {
+ return;
+ }
+
+ dbri = (struct dbri *) drivers[dev].private;
+
+ if (size > (1 << 14) - 1) {
+ printk("dbri_brecv called with size=%ld; truncated", size);
+ size = (1 << 14) - 1;
+ }
+
+ /* Make sure size is a multiple of four */
+ size &= ~3;
+
+ n = dbri_cmdlock(dbri);
+
+ /* XXX - Shouldn't I check to make sure RD isn't is use? */
+
+ dbri->B[chan].rd.flags = 0;
+ dbri->B[chan].rd.ba = (__u32) buffer;
+ dbri->B[chan].rd.nda = 0;
+ dbri->B[chan].rd.status = DBRI_RD_B | DBRI_RD_BCNT(size);
+
+ /* Pipe 8/9 is B1/B2 channel receive */
+ val = dbri->B[chan].recvSDP;
+ dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val);
+ dbri->cmd[n++] = (__u32)&dbri->B[chan].rd;
+
+ dbri_cmdsend(dbri, n);
+
+ dbri->B[chan].input_callback = callback;
+ dbri->B[chan].input_callback_arg = callback_arg;
+}
+
+#if defined(DBRI_ISDN) || defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x200ff
+struct foreign_interface dbri_foreign_interface = {
+ dbri_get_irqnum,
+ dbri_get_liu_state,
+ dbri_liu_init,
+ dbri_liu_activate,
+ dbri_liu_deactivate,
+ dbri_dxmit,
+ dbri_drecv,
+ dbri_bopen,
+ dbri_bclose,
+ dbri_bxmit,
+ dbri_brecv
+};
+EXPORT_SYMBOL(dbri_foreign_interface);
+#endif
+
+/*
+****************************************************************************
+**************************** Initialization ********************************
+****************************************************************************
+*/
static int dbri_attach(struct sparcaudio_driver *drv,
struct linux_sbus_device *sdev)
@@ -642,7 +1314,7 @@ static int dbri_attach(struct sparcaudio_driver *drv,
}
/* Register ourselves with the midlevel audio driver. */
- err = register_sparcaudio_driver(drv);
+ err = register_sparcaudio_driver(drv,1);
if (err) {
printk(KERN_ERR "DBRI: unable to register audio\n");
free_irq(dbri->irq, drv);
@@ -651,7 +1323,7 @@ static int dbri_attach(struct sparcaudio_driver *drv,
return err;
}
- dbri_init(drv);
+ dbri_initialize(drv);
err = mmcodec_init(drv);
if(err) {
dbri_detach(drv);
@@ -713,3 +1385,22 @@ 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/sbus/audio/dbri.h b/drivers/sbus/audio/dbri.h
index 098616ac8..af3a8a4d7 100644
--- a/drivers/sbus/audio/dbri.h
+++ b/drivers/sbus/audio/dbri.h
@@ -33,6 +33,17 @@ struct dbri_mem {
__u32 status;
};
+struct dbri_channel {
+ struct dbri_mem td;
+ struct dbri_mem rd;
+ unsigned int recvSDP;
+ unsigned int xmitSDP;
+ void (*output_callback)(void *, int);
+ void *output_callback_arg;
+ void (*input_callback)(void *, int, unsigned int);
+ void *input_callback_arg;
+};
+
#include "cs4215.h"
/* This structure holds the information for both chips (DBRI & CS4215) */
@@ -49,6 +60,15 @@ struct dbri {
struct wait_queue *wait, *int_wait; /* Where to sleep if busy */
struct audio_info perchip_info;
+
+ /* Track ISDN LIU and notify changes */
+ int liu_state;
+ void (*liu_callback)(void *);
+ void *liu_callback_arg;
+
+ /* Callback routines and descriptors for ISDN channels */
+ struct dbri_channel D;
+ struct dbri_channel B[2];
};
@@ -106,11 +126,10 @@ struct dbri {
#define D_TEST 0xd /* No comment */
#define D_CDM 0xe /* CHI Data mode command */
-#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
/* Special bits for some commands */
-#define D_PIPE(v) (v<<0) /* Pipe Nr: 0-15 long, 16-21 short */
+#define D_PIPE(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
/* Setup Data Pipe */
/* IRM */
@@ -140,8 +159,8 @@ struct dbri {
#define D_DTS_VO (1<<16) /* Valid Output Time-Slot Descriptor */
#define D_DTS_INS (1<<15) /* Insert Time Slot */
#define D_DTS_DEL (0<<15) /* Delete Time Slot */
-#define D_DTS_PRVIN(v) (v<<10) /* Previous In Pipe */
-#define D_DTS_PRVOUT(v) (v<<5) /* Previous Out Pipe */
+#define D_DTS_PRVIN(v) ((v)<<10) /* Previous In Pipe */
+#define D_DTS_PRVOUT(v) ((v)<<5) /* Previous Out Pipe */
/* Time Slot defines */
#define D_TS_LEN(v) (v<<24) /* Number of bits in this time slot */
@@ -151,8 +170,8 @@ struct dbri {
#define D_TS_MONITOR (2<<10) /* Monitor pipe */
#define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */
#define D_TS_ANCHOR (7<<10) /* Starting short pipes */
-#define D_TS_MON(v) (v<<5) /* Monitor Pipe */
-#define D_TS_NEXT(v) (v<<0) /* Pipe Nr: 0-15 long, 16-21 short */
+#define D_TS_MON(v) ((v)<<5) /* Monitor Pipe */
+#define D_TS_NEXT(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */
/* Concentration Highway Interface Modes */
#define D_CHI_CHICM(v) (v<<16) /* Clock mode */
@@ -268,7 +287,7 @@ struct dbri {
/* Transmit descriptor defines */
#define DBRI_TD_F (1<<31) /* End of Frame */
-#define DBRI_TD_D (1<<31) /* Do not append CRC */
+#define DBRI_TD_D (1<<30) /* Do not append CRC */
#define DBRI_TD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */
#define DBRI_TD_B (1<<15) /* Final interrupt */
#define DBRI_TD_M (1<<14) /* Marker interrupt */
@@ -277,17 +296,19 @@ struct dbri {
#define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */
#define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */
#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */
+#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */
/* Receive descriptor defines */
#define DBRI_RD_F (1<<31) /* End of Frame */
#define DBRI_RD_C (1<<30) /* Completed buffer */
#define DBRI_RD_B (1<<15) /* Final interrupt */
#define DBRI_RD_M (1<<14) /* Marker interrupt */
-#define DBRI_RD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */
#define DBRI_RD_BCNT(v) v /* Buffer size */
#define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */
#define DBRI_RD_BBC (1<<6) /* 1: Bad Byte recieved */
#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */
#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */
+#define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */
+#define DBRI_RD_CNT(v) ((v>>16)&0x1fff) /* Number of valid bytes in the buffer */
#endif /* _DBRI_H_ */