Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
21f59c57
Commit
21f59c57
authored
Sep 22, 2002
by
David S. Miller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[SPARC]: Blow away old sbus audio layer.
parent
2ee094ef
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
8 additions
and
11839 deletions
+8
-11839
arch/sparc/config.in
arch/sparc/config.in
+8
-3
arch/sparc64/config.in
arch/sparc64/config.in
+0
-1
drivers/sbus/audio/Config.help
drivers/sbus/audio/Config.help
+0
-24
drivers/sbus/audio/Config.in
drivers/sbus/audio/Config.in
+0
-18
drivers/sbus/audio/Makefile
drivers/sbus/audio/Makefile
+0
-16
drivers/sbus/audio/amd7930.c
drivers/sbus/audio/amd7930.c
+0
-1889
drivers/sbus/audio/amd7930.h
drivers/sbus/audio/amd7930.h
+0
-270
drivers/sbus/audio/audio.c
drivers/sbus/audio/audio.c
+0
-2304
drivers/sbus/audio/cs4215.h
drivers/sbus/audio/cs4215.h
+0
-120
drivers/sbus/audio/cs4231.c
drivers/sbus/audio/cs4231.c
+0
-2458
drivers/sbus/audio/cs4231.h
drivers/sbus/audio/cs4231.h
+0
-313
drivers/sbus/audio/dbri.c
drivers/sbus/audio/dbri.c
+0
-2400
drivers/sbus/audio/dbri.h
drivers/sbus/audio/dbri.h
+0
-357
drivers/sbus/audio/dmy.c
drivers/sbus/audio/dmy.c
+0
-805
drivers/sbus/audio/dummy.h
drivers/sbus/audio/dummy.h
+0
-41
include/asm-sparc/audioio.h
include/asm-sparc/audioio.h
+0
-410
include/asm-sparc64/audioio.h
include/asm-sparc64/audioio.h
+0
-410
No files found.
arch/sparc/config.in
View file @
21f59c57
...
...
@@ -70,7 +70,6 @@ source drivers/mtd/Config.in
source drivers/serial/Config.in
if [ "$CONFIG_SUN4" != "y" ]; then
source drivers/sbus/char/Config.in
source drivers/sbus/audio/Config.in
fi
mainmenu_option next_comment
...
...
@@ -223,9 +222,15 @@ source drivers/input/Config.in
source fs/Config.in
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
source net/bluetooth/Config.in
tristate 'Sound card support' CONFIG_SOUND
if [ "$CONFIG_SOUND" != "n" ]; then
source sound/Config.in
fi
endmenu
source drivers/usb/Config.in
source net/bluetooth/Config.in
mainmenu_option next_comment
comment 'Watchdog'
...
...
arch/sparc64/config.in
View file @
21f59c57
...
...
@@ -78,7 +78,6 @@ endmenu
source drivers/serial/Config.in
source drivers/sbus/char/Config.in
source drivers/sbus/audio/Config.in
source drivers/mtd/Config.in
mainmenu_option next_comment
comment 'Block devices'
...
...
drivers/sbus/audio/Config.help
deleted
100644 → 0
View file @
2ee094ef
CONFIG_SPARCAUDIO
This driver provides support for the build-in sound devices on most
Sun machines. If you want to be able to use this, select this option
and one or more of the lowlevel drivers below. See
<http://www.dementia.org/~shadow/sparcaudio.html> for more
information.
CONFIG_SPARCAUDIO_AMD7930
This driver supports the AMD 7930 chip found on sun4c, 4/6xx, and
SparcClassic systems.
CONFIG_SPARCAUDIO_CS4231
This driver supports the Crystal Semiconductor CS4231 chip found on
the SS4, SS5, and Ultras.
CONFIG_SPARCAUDIO_DBRI
This driver supports the DBRI audio interface found on the SS10,
SS20, Sparcbook 3, and Voyager systems.
CONFIG_SPARCAUDIO_DUMMY
This is a pseudo-driver used for debugging and testing the
sparcaudio subsystem. Say N unless you want to work on this
subsystem.
drivers/sbus/audio/Config.in
deleted
100644 → 0
View file @
2ee094ef
#
# Configuration script for sparcaudio subsystem
#
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
mainmenu_option next_comment
comment 'Linux/SPARC audio subsystem (EXPERIMENTAL)'
tristate 'Audio support (EXPERIMENTAL)' CONFIG_SPARCAUDIO
if [ "$CONFIG_SPARC64" != "y" ]; then
dep_tristate ' AMD7930 Lowlevel Driver' CONFIG_SPARCAUDIO_AMD7930 $CONFIG_SPARCAUDIO
dep_tristate ' DBRI Lowlevel Driver' CONFIG_SPARCAUDIO_DBRI $CONFIG_SPARCAUDIO
fi
dep_tristate ' CS4231 Lowlevel Driver' CONFIG_SPARCAUDIO_CS4231 $CONFIG_SPARCAUDIO
dep_tristate ' Dummy Lowlevel Driver' CONFIG_SPARCAUDIO_DUMMY $CONFIG_SPARCAUDIO
endmenu
fi
drivers/sbus/audio/Makefile
deleted
100644 → 0
View file @
2ee094ef
#
# Makefile for the kernel SPARC audio drivers.
#
# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
# Rewritten to use lists instead of if-statements.
#
export-objs
:=
audio.o amd7930.o dbri.o
obj-$(CONFIG_SPARCAUDIO)
+=
audio.o
obj-$(CONFIG_SPARCAUDIO_AMD7930)
+=
amd7930.o
obj-$(CONFIG_SPARCAUDIO_DBRI)
+=
dbri.o
obj-$(CONFIG_SPARCAUDIO_CS4231)
+=
cs4231.o
obj-$(CONFIG_SPARCAUDIO_DUMMY)
+=
dmy.o
include
$(TOPDIR)/Rules.make
drivers/sbus/audio/amd7930.c
deleted
100644 → 0
View file @
2ee094ef
/* $Id: amd7930.c,v 1.28 2001/10/13 01:47:29 davem Exp $
* drivers/sbus/audio/amd7930.c
*
* Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
*
* This is the lowlevel driver for the AMD7930 audio chip found on all
* sun4c machines and some sun4m machines.
*
* The amd7930 is actually an ISDN chip which has a very simple
* integrated audio encoder/decoder. When Sun decided on what chip to
* use for audio, they had the brilliant idea of using the amd7930 and
* only connecting the audio encoder/decoder pins.
*
* 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>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.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>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/audioio.h>
#include "amd7930.h"
static
__u8
bilinear2mulaw
(
__u8
data
);
static
__u8
mulaw2bilinear
(
__u8
data
);
static
__u8
linear2mulaw
(
__u16
data
);
static
__u16
mulaw2linear
(
__u8
data
);
#if defined (AMD79C30_ISDN)
#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
];
static
int
num_drivers
;
/* Each amd7930 chip has two bi-directional B channels and a D
* channel available to the uproc. This structure handles all
* the buffering needed to transmit and receive via a single channel.
*/
#define CHANNEL_AVAILABLE 0x00
#define CHANNEL_INUSE_AUDIO_IN 0x01
#define CHANNEL_INUSE_AUDIO_OUT 0x02
#define CHANNEL_INUSE_ISDN_B1 0x04
#define CHANNEL_INUSE_ISDN_B2 0x08
#define CHANNEL_INUSE 0xff
struct
amd7930_channel
{
/* Channel status */
u8
channel_status
;
/* Current buffer that the driver is playing on channel */
volatile
__u8
*
output_ptr
;
volatile
u32
output_count
;
u8
xmit_idle_char
;
/* Callback routine (and argument) when output is done on */
void
(
*
output_callback
)(
void
*
,
unsigned
char
);
void
*
output_callback_arg
;
/* Current buffer that the driver is recording on channel */
volatile
__u8
*
input_ptr
;
volatile
u32
input_count
;
volatile
u32
input_limit
;
/* Callback routine (and argument) when input is done on */
void
(
*
input_callback
)(
void
*
,
unsigned
char
,
unsigned
long
);
void
*
input_callback_arg
;
int
input_format
;
int
output_format
;
};
/* Private information we store for each amd7930 chip. */
struct
amd7930_info
{
struct
amd7930_channel
D
;
struct
amd7930_channel
Bb
;
struct
amd7930_channel
Bc
;
/* Pointers to which B channels are being used for what
* These three fields (Baudio, Bisdn[0], and Bisdn[1]) will either
* be NULL or point to one of the Bb/Bc structures above.
*/
struct
amd7930_channel
*
Baudio
;
struct
amd7930_channel
*
Bisdn
[
2
];
/* Device registers information. */
unsigned
long
regs
;
unsigned
long
regs_size
;
struct
amd7930_map
map
;
/* Volume information. */
int
pgain
,
rgain
,
mgain
;
/* Device interrupt information. */
int
irq
;
volatile
int
ints_on
;
/* Format type */
int
format_type
;
/* Someone to signal when the ISDN LIU state changes */
int
liu_state
;
void
(
*
liu_callback
)(
void
*
);
void
*
liu_callback_arg
;
};
/* Output a 16-bit quantity in the order that the amd7930 expects. */
static
__inline__
void
amd7930_out16
(
unsigned
long
regs
,
u16
val
)
{
sbus_writeb
(
val
&
0xff
,
regs
+
DR
);
sbus_writeb
(
val
>>
8
,
regs
+
DR
);
}
/* gx, gr & stg gains. this table must contain 256 elements with
* the 0th being "infinity" (the magic value 9008). The remaining
* elements match sun's gain curve (but with higher resolution):
* -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
*/
static
__const__
__u16
gx_coeff
[
256
]
=
{
0x9008
,
0x8b7c
,
0x8b51
,
0x8b45
,
0x8b42
,
0x8b3b
,
0x8b36
,
0x8b33
,
0x8b32
,
0x8b2a
,
0x8b2b
,
0x8b2c
,
0x8b25
,
0x8b23
,
0x8b22
,
0x8b22
,
0x9122
,
0x8b1a
,
0x8aa3
,
0x8aa3
,
0x8b1c
,
0x8aa6
,
0x912d
,
0x912b
,
0x8aab
,
0x8b12
,
0x8aaa
,
0x8ab2
,
0x9132
,
0x8ab4
,
0x913c
,
0x8abb
,
0x9142
,
0x9144
,
0x9151
,
0x8ad5
,
0x8aeb
,
0x8a79
,
0x8a5a
,
0x8a4a
,
0x8b03
,
0x91c2
,
0x91bb
,
0x8a3f
,
0x8a33
,
0x91b2
,
0x9212
,
0x9213
,
0x8a2c
,
0x921d
,
0x8a23
,
0x921a
,
0x9222
,
0x9223
,
0x922d
,
0x9231
,
0x9234
,
0x9242
,
0x925b
,
0x92dd
,
0x92c1
,
0x92b3
,
0x92ab
,
0x92a4
,
0x92a2
,
0x932b
,
0x9341
,
0x93d3
,
0x93b2
,
0x93a2
,
0x943c
,
0x94b2
,
0x953a
,
0x9653
,
0x9782
,
0x9e21
,
0x9d23
,
0x9cd2
,
0x9c23
,
0x9baa
,
0x9bde
,
0x9b33
,
0x9b22
,
0x9b1d
,
0x9ab2
,
0xa142
,
0xa1e5
,
0x9a3b
,
0xa213
,
0xa1a2
,
0xa231
,
0xa2eb
,
0xa313
,
0xa334
,
0xa421
,
0xa54b
,
0xada4
,
0xac23
,
0xab3b
,
0xaaab
,
0xaa5c
,
0xb1a3
,
0xb2ca
,
0xb3bd
,
0xbe24
,
0xbb2b
,
0xba33
,
0xc32b
,
0xcb5a
,
0xd2a2
,
0xe31d
,
0x0808
,
0x72ba
,
0x62c2
,
0x5c32
,
0x52db
,
0x513e
,
0x4cce
,
0x43b2
,
0x4243
,
0x41b4
,
0x3b12
,
0x3bc3
,
0x3df2
,
0x34bd
,
0x3334
,
0x32c2
,
0x3224
,
0x31aa
,
0x2a7b
,
0x2aaa
,
0x2b23
,
0x2bba
,
0x2c42
,
0x2e23
,
0x25bb
,
0x242b
,
0x240f
,
0x231a
,
0x22bb
,
0x2241
,
0x2223
,
0x221f
,
0x1a33
,
0x1a4a
,
0x1acd
,
0x2132
,
0x1b1b
,
0x1b2c
,
0x1b62
,
0x1c12
,
0x1c32
,
0x1d1b
,
0x1e71
,
0x16b1
,
0x1522
,
0x1434
,
0x1412
,
0x1352
,
0x1323
,
0x1315
,
0x12bc
,
0x127a
,
0x1235
,
0x1226
,
0x11a2
,
0x1216
,
0x0a2a
,
0x11bc
,
0x11d1
,
0x1163
,
0x0ac2
,
0x0ab2
,
0x0aab
,
0x0b1b
,
0x0b23
,
0x0b33
,
0x0c0f
,
0x0bb3
,
0x0c1b
,
0x0c3e
,
0x0cb1
,
0x0d4c
,
0x0ec1
,
0x079a
,
0x0614
,
0x0521
,
0x047c
,
0x0422
,
0x03b1
,
0x03e3
,
0x0333
,
0x0322
,
0x031c
,
0x02aa
,
0x02ba
,
0x02f2
,
0x0242
,
0x0232
,
0x0227
,
0x0222
,
0x021b
,
0x01ad
,
0x0212
,
0x01b2
,
0x01bb
,
0x01cb
,
0x01f6
,
0x0152
,
0x013a
,
0x0133
,
0x0131
,
0x012c
,
0x0123
,
0x0122
,
0x00a2
,
0x011b
,
0x011e
,
0x0114
,
0x00b1
,
0x00aa
,
0x00b3
,
0x00bd
,
0x00ba
,
0x00c5
,
0x00d3
,
0x00f3
,
0x0062
,
0x0051
,
0x0042
,
0x003b
,
0x0033
,
0x0032
,
0x002a
,
0x002c
,
0x0025
,
0x0023
,
0x0022
,
0x001a
,
0x0021
,
0x001b
,
0x001b
,
0x001d
,
0x0015
,
0x0013
,
0x0013
,
0x0012
,
0x0012
,
0x000a
,
0x000a
,
0x0011
,
0x0011
,
0x000b
,
0x000b
,
0x000c
,
0x000e
,
};
static
__const__
__u16
ger_coeff
[]
=
{
0x431f
,
/* 5. dB */
0x331f
,
/* 5.5 dB */
0x40dd
,
/* 6. dB */
0x11dd
,
/* 6.5 dB */
0x440f
,
/* 7. dB */
0x411f
,
/* 7.5 dB */
0x311f
,
/* 8. dB */
0x5520
,
/* 8.5 dB */
0x10dd
,
/* 9. dB */
0x4211
,
/* 9.5 dB */
0x410f
,
/* 10. dB */
0x111f
,
/* 10.5 dB */
0x600b
,
/* 11. dB */
0x00dd
,
/* 11.5 dB */
0x4210
,
/* 12. dB */
0x110f
,
/* 13. dB */
0x7200
,
/* 14. dB */
0x2110
,
/* 15. dB */
0x2200
,
/* 15.9 dB */
0x000b
,
/* 16.9 dB */
0x000f
/* 18. dB */
};
#define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
/* Enable amd7930 interrupts atomically. */
static
void
amd7930_enable_ints
(
struct
amd7930_info
*
info
)
{
unsigned
long
flags
;
save_and_cli
(
flags
);
if
(
!
info
->
ints_on
)
{
sbus_writeb
(
AMR_INIT
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_INIT_ACTIVE
,
info
->
regs
+
DR
);
info
->
ints_on
=
1
;
}
restore_flags
(
flags
);
}
/* Disable amd7930 interrupts atomically. */
static
__inline__
void
amd7930_disable_ints
(
struct
amd7930_info
*
info
)
{
unsigned
long
flags
;
save_and_cli
(
flags
);
if
(
info
->
ints_on
)
{
sbus_writeb
(
AMR_INIT
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_INIT_ACTIVE
|
AM_INIT_DISABLE_INTS
,
info
->
regs
+
DR
);
info
->
ints_on
=
0
;
}
restore_flags
(
flags
);
}
/* Idle amd7930 (no interrupts, no audio, no data) */
static
__inline__
void
amd7930_idle
(
struct
amd7930_info
*
info
)
{
unsigned
long
flags
;
save_and_cli
(
flags
);
if
(
info
->
ints_on
)
{
sbus_writeb
(
AMR_INIT
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
info
->
ints_on
=
0
;
}
restore_flags
(
flags
);
}
/* Commit the local copy of the MAP registers to the amd7930. */
static
void
amd7930_write_map
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
unsigned
long
regs
=
info
->
regs
;
struct
amd7930_map
*
map
=
&
info
->
map
;
unsigned
long
flags
;
save_and_cli
(
flags
);
sbus_writeb
(
AMR_MAP_GX
,
regs
+
CR
);
amd7930_out16
(
regs
,
map
->
gx
);
sbus_writeb
(
AMR_MAP_GR
,
regs
+
CR
);
amd7930_out16
(
regs
,
map
->
gr
);
sbus_writeb
(
AMR_MAP_STGR
,
regs
+
CR
);
amd7930_out16
(
regs
,
map
->
stgr
);
sbus_writeb
(
AMR_MAP_GER
,
regs
+
CR
);
amd7930_out16
(
regs
,
map
->
ger
);
sbus_writeb
(
AMR_MAP_MMR1
,
regs
+
CR
);
sbus_writeb
(
map
->
mmr1
,
regs
+
DR
);
sbus_writeb
(
AMR_MAP_MMR2
,
regs
+
CR
);
sbus_writeb
(
map
->
mmr2
,
regs
+
DR
);
restore_flags
(
flags
);
}
/* Update the MAP registers with new settings. */
static
void
amd7930_update_map
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
struct
amd7930_map
*
map
=
&
info
->
map
;
int
level
;
map
->
gx
=
gx_coeff
[
info
->
rgain
];
map
->
stgr
=
gx_coeff
[
info
->
mgain
];
level
=
(
info
->
pgain
*
(
256
+
NR_GER_COEFFS
))
>>
8
;
if
(
level
>=
256
)
{
map
->
ger
=
ger_coeff
[
level
-
256
];
map
->
gr
=
gx_coeff
[
255
];
}
else
{
map
->
ger
=
ger_coeff
[
0
];
map
->
gr
=
gx_coeff
[
level
];
}
amd7930_write_map
(
drv
);
}
/* Bit of a hack here - if the HISAX ISDN driver has got INTSTAT debugging
* turned on, we send debugging characters to the ISDN driver:
*
* i# - Interrupt received - number from 0 to 7 is low three bits of IR
* > - Loaded a single char into the Dchan xmit FIFO
* + - Finished loading an xmit packet into the Dchan xmit FIFO
* < - Read a single char from the Dchan recv FIFO
* ! - Finished reading a packet from the Dchan recv FIFO
*
* This code needs to be removed if anything other than HISAX uses the ISDN
* driver, since D.output_callback_arg is assumed to be a certain struct ptr
*/
#ifdef L2FRAME_DEBUG
inline
void
debug_info
(
struct
amd7930_info
*
info
,
char
c
)
{
struct
IsdnCardState
*
cs
;
if
(
!
info
||
!
info
->
D
.
output_callback_arg
)
return
;
cs
=
(
struct
IsdnCardState
*
)
info
->
D
.
output_callback_arg
;
if
(
!
cs
||
!
cs
->
status_write
)
return
;
if
(
cs
->
debug
&
L1_DEB_INTSTAT
)
{
*
(
cs
->
status_write
++
)
=
c
;
if
(
cs
->
status_write
>
cs
->
status_end
)
cs
->
status_write
=
cs
->
status_buf
;
}
}
#else
#define debug_info(info,c)
#endif
static
void
fill_D_xmit_fifo
(
struct
amd7930_info
*
info
)
{
/* Send next byte(s) of outgoing data. */
while
(
info
->
D
.
output_ptr
&&
info
->
D
.
output_count
>
0
&&
(
sbus_readb
(
info
->
regs
+
DSR2
)
&
AMR_DSR2_TBE
))
{
u8
byte
=
*
(
info
->
D
.
output_ptr
);
/* Send the next byte and advance buffer pointer. */
sbus_writeb
(
byte
,
info
->
regs
+
DCTB
);
info
->
D
.
output_ptr
++
;
info
->
D
.
output_count
--
;
debug_info
(
info
,
'>'
);
}
}
static
void
transceive_Dchannel
(
struct
amd7930_info
*
info
)
{
__u8
dummy
;
#define D_XMIT_ERRORS (AMR_DER_COLLISION | AMR_DER_UNRN)
#define D_RECV_ERRORS (AMR_DER_RABRT | AMR_DER_RFRAME | AMR_DER_FCS | \
AMR_DER_OVFL | AMR_DER_UNFL | AMR_DER_OVRN)
/* Transmit if we can */
fill_D_xmit_fifo
(
info
);
/* Done with the xmit buffer? Notify the midlevel driver. */
if
(
info
->
D
.
output_ptr
!=
NULL
&&
info
->
D
.
output_count
==
0
)
{
info
->
D
.
output_ptr
=
NULL
;
info
->
D
.
output_count
=
0
;
debug_info
(
info
,
'+'
);
if
(
info
->
D
.
output_callback
)
(
*
info
->
D
.
output_callback
)
(
info
->
D
.
output_callback_arg
,
sbus_readb
(
info
->
regs
+
DER
));
/* sbus_readb(info->regs + DER) & D_XMIT_ERRORS); */
}
/* Read the next byte(s) of incoming data. */
while
(
sbus_readb
(
info
->
regs
+
DSR2
)
&
AMR_DSR2_RBA
)
{
if
(
info
->
D
.
input_ptr
&&
(
info
->
D
.
input_count
<
info
->
D
.
input_limit
))
{
/* Get the next byte and advance buffer pointer. */
*
(
info
->
D
.
input_ptr
)
=
sbus_readb
(
info
->
regs
+
DCRB
);
info
->
D
.
input_ptr
++
;
info
->
D
.
input_count
++
;
}
else
{
/* Overflow - should be detected by chip via RBLR
* so we'll just consume data until we see LBRP
*/
dummy
=
sbus_readb
(
info
->
regs
+
DCRB
);
}
debug_info
(
info
,
'<'
);
if
(
sbus_readb
(
info
->
regs
+
DSR2
)
&
AMR_DSR2_LBRP
)
{
__u8
der
;
/* End of recv packet? Notify the midlevel driver. */
debug_info
(
info
,
'!'
);
info
->
D
.
input_ptr
=
NULL
;
der
=
sbus_readb
(
info
->
regs
+
DER
)
&
D_RECV_ERRORS
;
/* Read receive byte count - advances FIFOs */
sbus_writeb
(
AMR_DLC_DRCR
,
info
->
regs
+
CR
);
dummy
=
sbus_readb
(
info
->
regs
+
DR
);
dummy
=
sbus_readb
(
info
->
regs
+
DR
);
if
(
info
->
D
.
input_callback
)
(
*
info
->
D
.
input_callback
)
(
info
->
D
.
input_callback_arg
,
der
,
info
->
D
.
input_count
);
}
}
}
long
amd7930_xmit_idles
=
0
;
static
void
transceive_Bchannel
(
struct
amd7930_channel
*
channel
,
unsigned
long
reg
)
{
/* Send the next byte of outgoing data. */
if
(
channel
->
output_ptr
&&
channel
->
output_count
>
0
)
{
u8
byte
;
/* Send the next byte and advance buffer pointer. */
switch
(
channel
->
output_format
)
{
case
AUDIO_ENCODING_ULAW
:
case
AUDIO_ENCODING_ALAW
:
byte
=
*
(
channel
->
output_ptr
);
sbus_writeb
(
byte
,
reg
);
break
;
case
AUDIO_ENCODING_LINEAR8
:
byte
=
bilinear2mulaw
(
*
(
channel
->
output_ptr
));
sbus_writeb
(
byte
,
reg
);
break
;
case
AUDIO_ENCODING_LINEAR
:
if
(
channel
->
output_count
>=
2
)
{
u16
val
=
channel
->
output_ptr
[
0
]
<<
8
;
val
|=
channel
->
output_ptr
[
1
];
byte
=
linear2mulaw
(
val
);
sbus_writeb
(
byte
,
reg
);
channel
->
output_ptr
++
;
channel
->
output_count
--
;
};
};
channel
->
output_ptr
++
;
channel
->
output_count
--
;
/* Done with the buffer? Notify the midlevel driver. */
if
(
channel
->
output_count
==
0
)
{
channel
->
output_ptr
=
NULL
;
channel
->
output_count
=
0
;
if
(
channel
->
output_callback
)
(
*
channel
->
output_callback
)
(
channel
->
output_callback_arg
,
1
);
}
}
else
{
sbus_writeb
(
channel
->
xmit_idle_char
,
reg
);
amd7930_xmit_idles
++
;
}
/* Read the next byte of incoming data. */
if
(
channel
->
input_ptr
&&
channel
->
input_count
>
0
)
{
/* Get the next byte and advance buffer pointer. */
switch
(
channel
->
input_format
)
{
case
AUDIO_ENCODING_ULAW
:
case
AUDIO_ENCODING_ALAW
:
*
(
channel
->
input_ptr
)
=
sbus_readb
(
reg
);
break
;
case
AUDIO_ENCODING_LINEAR8
:
*
(
channel
->
input_ptr
)
=
mulaw2bilinear
(
sbus_readb
(
reg
));
break
;
case
AUDIO_ENCODING_LINEAR
:
if
(
channel
->
input_count
>=
2
)
{
u16
val
=
mulaw2linear
(
sbus_readb
(
reg
));
channel
->
input_ptr
[
0
]
=
val
>>
8
;
channel
->
input_ptr
[
1
]
=
val
&
0xff
;
channel
->
input_ptr
++
;
channel
->
input_count
--
;
}
else
{
*
(
channel
->
input_ptr
)
=
0
;
}
};
channel
->
input_ptr
++
;
channel
->
input_count
--
;
/* Done with the buffer? Notify the midlevel driver. */
if
(
channel
->
input_count
==
0
)
{
channel
->
input_ptr
=
NULL
;
channel
->
input_count
=
0
;
if
(
channel
->
input_callback
)
(
*
channel
->
input_callback
)
(
channel
->
input_callback_arg
,
1
,
0
);
}
}
}
/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */
static
void
amd7930_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
intr_regs
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
dev_id
;
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
unsigned
long
regs
=
info
->
regs
;
__u8
ir
;
/* Clear the interrupt. */
ir
=
sbus_readb
(
regs
+
IR
);
if
(
ir
&
AMR_IR_BBUF
)
{
if
(
info
->
Bb
.
channel_status
==
CHANNEL_INUSE
)
transceive_Bchannel
(
&
info
->
Bb
,
info
->
regs
+
BBTB
);
if
(
info
->
Bc
.
channel_status
==
CHANNEL_INUSE
)
transceive_Bchannel
(
&
info
->
Bc
,
info
->
regs
+
BCTB
);
}
if
(
ir
&
(
AMR_IR_DRTHRSH
|
AMR_IR_DTTHRSH
|
AMR_IR_DSRI
))
{
debug_info
(
info
,
'i'
);
debug_info
(
info
,
'0'
+
(
ir
&
7
));
transceive_Dchannel
(
info
);
}
if
(
ir
&
AMR_IR_LSRI
)
{
__u8
lsr
;
sbus_writeb
(
AMR_LIU_LSR
,
regs
+
CR
);
lsr
=
sbus_readb
(
regs
+
DR
);
info
->
liu_state
=
(
lsr
&
0x7
)
+
2
;
if
(
info
->
liu_callback
)
(
*
info
->
liu_callback
)(
info
->
liu_callback_arg
);
}
}
static
int
amd7930_open
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
switch
(
MINOR
(
inode
->
i_rdev
)
&
0xf
)
{
case
SPARCAUDIO_AUDIO_MINOR
:
info
->
format_type
=
AUDIO_ENCODING_ULAW
;
break
;
case
SPARCAUDIO_DSP_MINOR
:
info
->
format_type
=
AUDIO_ENCODING_LINEAR8
;
break
;
case
SPARCAUDIO_DSP16_MINOR
:
info
->
format_type
=
AUDIO_ENCODING_LINEAR
;
break
;
};
MOD_INC_USE_COUNT
;
return
0
;
}
static
void
amd7930_release
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
/* amd7930_disable_ints(drv->private); */
MOD_DEC_USE_COUNT
;
}
static
void
request_Baudio
(
struct
amd7930_info
*
info
)
{
if
(
info
->
Bb
.
channel_status
==
CHANNEL_AVAILABLE
)
{
info
->
Bb
.
channel_status
=
CHANNEL_INUSE
;
info
->
Baudio
=
&
info
->
Bb
;
/* Multiplexor map - audio (Ba) to Bb */
sbus_writeb
(
AMR_MUX_MCR1
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_MUX_CHANNEL_Ba
|
(
AM_MUX_CHANNEL_Bb
<<
4
),
info
->
regs
+
DR
);
/* Enable B channel interrupts */
sbus_writeb
(
AMR_MUX_MCR4
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_MUX_MCR4_ENABLE_INTS
,
info
->
regs
+
DR
);
}
else
if
(
info
->
Bc
.
channel_status
==
CHANNEL_AVAILABLE
)
{
info
->
Bc
.
channel_status
=
CHANNEL_INUSE
;
info
->
Baudio
=
&
info
->
Bc
;
/* Multiplexor map - audio (Ba) to Bc */
sbus_writeb
(
AMR_MUX_MCR1
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_MUX_CHANNEL_Ba
|
(
AM_MUX_CHANNEL_Bc
<<
4
),
info
->
regs
+
DR
);
/* Enable B channel interrupts */
sbus_writeb
(
AMR_MUX_MCR4
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_MUX_MCR4_ENABLE_INTS
,
info
->
regs
+
DR
);
}
}
static
void
release_Baudio
(
struct
amd7930_info
*
info
)
{
if
(
info
->
Baudio
)
{
info
->
Baudio
->
channel_status
=
CHANNEL_AVAILABLE
;
sbus_writeb
(
AMR_MUX_MCR1
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
info
->
Baudio
=
NULL
;
if
(
info
->
Bb
.
channel_status
==
CHANNEL_AVAILABLE
&&
info
->
Bc
.
channel_status
==
CHANNEL_AVAILABLE
)
{
/* Disable B channel interrupts */
sbus_writeb
(
AMR_MUX_MCR4
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
}
}
}
static
void
amd7930_start_output
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
if
(
!
info
->
Baudio
)
request_Baudio
(
info
);
if
(
info
->
Baudio
)
{
info
->
Baudio
->
output_ptr
=
buffer
;
info
->
Baudio
->
output_count
=
count
;
info
->
Baudio
->
output_format
=
info
->
format_type
;
info
->
Baudio
->
output_callback
=
(
void
*
)
&
sparcaudio_output_done
;
info
->
Baudio
->
output_callback_arg
=
(
void
*
)
drv
;
info
->
Baudio
->
xmit_idle_char
=
0
;
}
}
static
void
amd7930_stop_output
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
if
(
info
->
Baudio
)
{
info
->
Baudio
->
output_ptr
=
NULL
;
info
->
Baudio
->
output_count
=
0
;
if
(
!
info
->
Baudio
->
input_ptr
)
release_Baudio
(
info
);
}
}
static
void
amd7930_start_input
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
if
(
!
info
->
Baudio
)
request_Baudio
(
info
);
if
(
info
->
Baudio
)
{
info
->
Baudio
->
input_ptr
=
buffer
;
info
->
Baudio
->
input_count
=
count
;
info
->
Baudio
->
input_format
=
info
->
format_type
;
info
->
Baudio
->
input_callback
=
(
void
*
)
&
sparcaudio_input_done
;
info
->
Baudio
->
input_callback_arg
=
(
void
*
)
drv
;
}
}
static
void
amd7930_stop_input
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
if
(
info
->
Baudio
)
{
info
->
Baudio
->
input_ptr
=
NULL
;
info
->
Baudio
->
input_count
=
0
;
if
(
!
info
->
Baudio
->
output_ptr
)
release_Baudio
(
info
);
}
}
static
void
amd7930_sunaudio_getdev
(
struct
sparcaudio_driver
*
drv
,
audio_device_t
*
audinfo
)
{
strncpy
(
audinfo
->
name
,
"SUNW,am79c30"
,
sizeof
(
audinfo
->
name
)
-
1
);
strncpy
(
audinfo
->
version
,
"a"
,
sizeof
(
audinfo
->
version
)
-
1
);
strncpy
(
audinfo
->
config
,
"onboard1"
,
sizeof
(
audinfo
->
config
)
-
1
);
}
static
int
amd7930_sunaudio_getdev_sunos
(
struct
sparcaudio_driver
*
drv
)
{
return
AUDIO_DEV_AMD
;
}
static
int
amd7930_get_formats
(
struct
sparcaudio_driver
*
drv
)
{
return
(
AFMT_MU_LAW
|
AFMT_A_LAW
|
AFMT_U8
|
AFMT_S16_BE
);
}
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
;
info
->
pgain
=
vol
;
amd7930_update_map
(
drv
);
return
0
;
}
static
int
amd7930_get_output_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
return
info
->
pgain
;
}
static
int
amd7930_set_input_volume
(
struct
sparcaudio_driver
*
drv
,
int
vol
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
info
->
rgain
=
vol
;
amd7930_update_map
(
drv
);
return
0
;
}
static
int
amd7930_get_input_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
return
info
->
rgain
;
}
static
int
amd7930_set_monitor_volume
(
struct
sparcaudio_driver
*
drv
,
int
vol
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
info
->
mgain
=
vol
;
amd7930_update_map
(
drv
);
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
)
{
return
AUDIO_MID_BALANCE
;
}
static
int
amd7930_get_input_balance
(
struct
sparcaudio_driver
*
drv
)
{
return
AUDIO_MID_BALANCE
;
}
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
;
}
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
)
{
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 */
static
int
amd7930_get_input_port
(
struct
sparcaudio_driver
*
drv
)
{
return
AUDIO_MICROPHONE
;
}
static
int
amd7930_get_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
if
((
info
->
map
.
mmr1
&
AM_MAP_MMR1_ALAW
)
&&
(
info
->
format_type
==
AUDIO_ENCODING_ALAW
))
return
AUDIO_ENCODING_ALAW
;
return
info
->
format_type
;
}
static
int
amd7930_set_encoding
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
switch
(
value
)
{
case
AUDIO_ENCODING_ALAW
:
info
->
map
.
mmr1
|=
AM_MAP_MMR1_ALAW
;
break
;
case
AUDIO_ENCODING_LINEAR8
:
case
AUDIO_ENCODING_LINEAR
:
case
AUDIO_ENCODING_ULAW
:
info
->
map
.
mmr1
&=
~
AM_MAP_MMR1_ALAW
;
break
;
default:
return
-
EINVAL
;
};
info
->
format_type
=
value
;
amd7930_update_map
(
drv
);
return
0
;
}
/* This is what you get. Take it or leave it */
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_set_input_rate
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
return
(
value
==
AMD7930_RATE
)
?
0
:
-
EINVAL
;
}
static
int
amd7930_get_output_muted
(
struct
sparcaudio_driver
*
drv
)
{
return
0
;
}
static
void
amd7930_loopback
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
value
)
{
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
);
}
static
int
amd7930_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
,
struct
sparcaudio_driver
*
drv
)
{
int
retval
=
0
;
switch
(
cmd
)
{
case
AUDIO_DIAG_LOOPBACK
:
amd7930_loopback
(
drv
,
(
unsigned
int
)
arg
);
break
;
default:
retval
=
-
EINVAL
;
};
return
retval
;
}
/*
* ISDN operations
*
* Many of these routines take an "int dev" argument, which is simply
* an index into the drivers[] array. Currently, we only support a
* single AMD 7930 chip, so the value should always be 0. B channel
* operations require an "int chan", which should be 0 for channel B1
* and 1 for channel B2
*
* int amd7930_get_irqnum(int dev)
*
* returns the interrupt number being used by the chip. ISDN4linux
* uses this number to watch the interrupt during initialization and
* make sure something is happening.
*
* int amd7930_get_liu_state(int dev)
*
* returns the current state of the ISDN Line Interface Unit (LIU)
* as a number between 2 (state F2) and 7 (state F7). 0 may also be
* returned if the chip doesn't exist or the LIU hasn't been
* activated. The meanings of the states are defined in I.430, ISDN
* BRI Physical Layer Interface. The most important two states are
* F3 (shutdown) and F7 (syncronized).
*
* void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg)
*
* initializes the LIU and optionally registers a callback to be
* signaled upon a change of LIU state. The callback will be called
* with a single opaque callback_arg Once the callback has been
* triggered, amd7930_get_liu_state can be used to determine the LIU
* current state.
*
* void amd7930_liu_activate(int dev, int priority)
*
* requests LIU activation at a given D-channel priority.
* Successful activatation is achieved upon entering state F7, which
* will trigger any callback previously registered with
* amd7930_liu_init.
*
* void amd7930_liu_deactivate(int dev)
*
* deactivates LIU. Outstanding D and B channel transactions are
* terminated rudely and without callback notification. LIU change
* of state callback will be triggered, however.
*
* void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count,
* void (*callback)(void *, int), void *callback_arg)
*
* transmits a packet - specified with buffer, count - over the D-channel
* interface. Buffer should begin with the LAPD address field and
* end with the information field. FCS and flag sequences should not
* be included, nor is bit-stuffing required - all these functions are
* performed by the chip. The callback function will be called
* DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed
* both the arbitrary callback_arg and an integer error indication:
*
* 0 - successful transmission; ready for next packet
* non-0 - error value from chip's DER (D-Channel Error Register):
* 4 - collision detect
* 128 - underrun; irq routine didn't service chip fast enough
*
* The callback routine should defer any time-consuming operations
* to a bottom-half handler; however, amd7930_dxmit may be called
* from within the callback to request back-to-back transmission of
* a second packet (without repeating the priority/collision mechanism)
*
* A comment about the "collision detect" error, which is signalled
* whenever the echoed D-channel data didn't match the transmitted
* data. This is part of ISDN's normal multi-drop T-interface
* operation, indicating that another device has attempted simultaneous
* transmission, but can also result from line noise. An immediate
* requeue via amd7930_dxmit is suggested, but repeated collision
* errors may indicate a more serious problem.
*
* void amd7930_drecv(int dev, __u8 *buffer, unsigned int size,
* void (*callback)(void *, int, unsigned int),
* void *callback_arg)
*
* register a buffer - buffer, size - into which a D-channel packet
* can be received. The callback function will be called DURING
* THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an
* arbitrary callback_arg, an integer error indication and the length
* of the received packet, which will start with the address field,
* end with the information field, and not contain flag or FCS
* bytes. Bit-stuffing will already have been corrected for.
* Possible values of second callback argument "error":
*
* 0 - successful reception
* non-0 - error value from chip's DER (D-Channel Error Register):
* 1 - received packet abort
* 2 - framing error; non-integer number of bytes received
* 8 - FCS error; CRC sequence indicated corrupted data
* 16 - overflow error; packet exceeded size of buffer
* 32 - underflow error; packet smaller than required five bytes
* 64 - overrun error; irq routine didn't service chip fast enough
*
* int amd7930_bopen(int dev, int chan, u_char xmit_idle_char)
*
* This function should be called before any other operations on a B
* channel. In addition to arranging for interrupt handling and
* channel multiplexing, it sets the xmit_idle_char which is
* transmitted on the interface when no data buffer is available.
* Suggested values are: 0 for ISDN audio; FF for HDLC mark idle; 7E
* for HDLC flag idle. Returns 0 on a successful open; -1 on error,
* which is quite possible if audio and the other ISDN channel are
* already in use, since the Am7930 can only send two of the three
* channels to the processor
*
* void amd7930_bclose(int dev, int chan)
*
* Shuts down a B channel when no longer in use.
*
* void amd7930_bxmit(int dev, int chan, __u8 *buffer, unsigned int count,
* void (*callback)(void *), void *callback_arg)
*
* transmits a raw data block - specified with buffer, count - over
* the B channel interface specified by dev/chan. The callback
* function will be called DURING THE TOP HALF OF AN INTERRUPT
* HANDLER and will be passed the arbitrary callback_arg
*
* The callback routine should defer any time-consuming operations
* to a bottom-half handler; however, amd7930_bxmit may be called
* from within the callback to request back-to-back transmission of
* another data block
*
* void amd7930_brecv(int dev, int chan, __u8 *buffer, unsigned int size,
* void (*callback)(void *), void *callback_arg)
*
* receive a raw data block - specified with buffer, size - over the
* B channel interface specified by dev/chan. The callback function
* will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and
* will be passed the arbitrary callback_arg
*
* The callback routine should defer any time-consuming operations
* to a bottom-half handler; however, amd7930_brecv may be called
* from within the callback to register another buffer and ensure
* continuous B channel reception without loss of data
*
*/
#if defined (AMD79C30_ISDN)
static
int
amd7930_get_irqnum
(
int
dev
)
{
struct
amd7930_info
*
info
;
if
(
dev
>
num_drivers
)
return
(
0
);
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
return
info
->
irq
;
}
static
int
amd7930_get_liu_state
(
int
dev
)
{
struct
amd7930_info
*
info
;
if
(
dev
>
num_drivers
)
return
(
0
);
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
return
info
->
liu_state
;
}
static
void
amd7930_liu_init
(
int
dev
,
void
(
*
callback
)(),
void
*
callback_arg
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
/* Set callback for LIU state change */
info
->
liu_callback
=
callback
;
info
->
liu_callback_arg
=
callback_arg
;
/* De-activate the ISDN Line Interface Unit (LIU) */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
/* Request interrupt when LIU changes state from/to F3/F7/F8 */
sbus_writeb
(
AMR_LIU_LMR2
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_LIU_LMR2_EN_F3_INT
|
AM_LIU_LMR2_EN_F7_INT
|
AM_LIU_LMR2_EN_F8_INT
,
info
->
regs
+
DR
);
/* amd7930_enable_ints(info); */
/* Activate the ISDN Line Interface Unit (LIU) */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_LIU_LMR1_LIU_ENABL
,
info
->
regs
+
DR
);
restore_flags
(
flags
);
}
static
void
amd7930_liu_activate
(
int
dev
,
int
priority
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
/* Set D-channel access priority
*
* I.430 defines a priority mechanism based on counting 1s
* in the echo channel before transmitting
*
* Priority 0 is eight 1s; priority 1 is ten 1s; etc
*/
sbus_writeb
(
AMR_LIU_LPR
,
info
->
regs
+
CR
);
sbus_writeb
(
priority
&
0x0f
,
info
->
regs
+
DR
);
/* request LIU activation */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_LIU_LMR1_LIU_ENABL
|
AM_LIU_LMR1_REQ_ACTIV
,
info
->
regs
+
DR
);
restore_flags
(
flags
);
}
static
void
amd7930_liu_deactivate
(
int
dev
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
/* deactivate LIU */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
restore_flags
(
flags
);
}
static
void
amd7930_dxmit
(
int
dev
,
__u8
*
buffer
,
unsigned
int
count
,
void
(
*
callback
)(
void
*
,
int
),
void
*
callback_arg
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
__u8
dmr1
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
if
(
info
->
D
.
output_ptr
)
{
restore_flags
(
flags
);
printk
(
"amd7930_dxmit: transmitter in use
\n
"
);
return
;
}
info
->
D
.
output_ptr
=
buffer
;
info
->
D
.
output_count
=
count
;
info
->
D
.
output_callback
=
callback
;
info
->
D
.
output_callback_arg
=
callback_arg
;
/* Enable D-channel Transmit Threshold interrupt; disable addressing */
sbus_writeb
(
AMR_DLC_DMR1
,
info
->
regs
+
CR
);
dmr1
=
sbus_readb
(
info
->
regs
+
DR
);
dmr1
|=
AMR_DLC_DMR1_DTTHRSH_INT
;
dmr1
&=
~
AMR_DLC_DMR1_EN_ADDRS
;
sbus_writeb
(
dmr1
,
info
->
regs
+
DR
);
/* Begin xmit by setting D-channel Transmit Byte Count Reg (DTCR) */
sbus_writeb
(
AMR_DLC_DTCR
,
info
->
regs
+
CR
);
sbus_writeb
(
count
&
0xff
,
info
->
regs
+
DR
);
sbus_writeb
((
count
>>
8
)
&
0xff
,
info
->
regs
+
DR
);
/* Prime xmit FIFO */
/* fill_D_xmit_fifo(info); */
transceive_Dchannel
(
info
);
restore_flags
(
flags
);
}
static
void
amd7930_drecv
(
int
dev
,
__u8
*
buffer
,
unsigned
int
size
,
void
(
*
callback
)(
void
*
,
int
,
unsigned
int
),
void
*
callback_arg
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
__u8
dmr1
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
if
(
info
->
D
.
input_ptr
)
{
restore_flags
(
flags
);
printk
(
"amd7930_drecv: receiver already has buffer!
\n
"
);
return
;
}
info
->
D
.
input_ptr
=
buffer
;
info
->
D
.
input_count
=
0
;
info
->
D
.
input_limit
=
size
;
info
->
D
.
input_callback
=
callback
;
info
->
D
.
input_callback_arg
=
callback_arg
;
/* Enable D-channel Receive Threshold interrupt;
* Enable D-channel End of Receive Packet interrupt;
* Disable address recognition
*/
sbus_writeb
(
AMR_DLC_DMR1
,
info
->
regs
+
CR
);
dmr1
=
sbus_readb
(
info
->
regs
+
DR
);
dmr1
|=
AMR_DLC_DMR1_DRTHRSH_INT
|
AMR_DLC_DMR1_EORP_INT
;
dmr1
&=
~
AMR_DLC_DMR1_EN_ADDRS
;
sbus_writeb
(
dmr1
,
info
->
regs
+
DR
);
/* Set D-channel Receive Byte Count Limit Register */
sbus_writeb
(
AMR_DLC_DRCR
,
info
->
regs
+
CR
);
sbus_writeb
(
size
&
0xff
,
info
->
regs
+
DR
);
sbus_writeb
((
size
>>
8
)
&
0xff
,
info
->
regs
+
DR
);
restore_flags
(
flags
);
}
static
int
amd7930_bopen
(
int
dev
,
unsigned
int
chan
,
int
mode
,
u_char
xmit_idle_char
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
u8
tmp
;
if
(
dev
>
num_drivers
||
chan
<
0
||
chan
>
1
)
return
-
1
;
if
(
mode
==
L1_MODE_HDLC
)
return
-
1
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
if
(
info
->
Bb
.
channel_status
==
CHANNEL_AVAILABLE
)
{
info
->
Bb
.
channel_status
=
CHANNEL_INUSE
;
info
->
Bb
.
xmit_idle_char
=
xmit_idle_char
;
info
->
Bisdn
[
chan
]
=
&
info
->
Bb
;
/* Multiplexor map - isdn (B1/2) to Bb */
sbus_writeb
(
AMR_MUX_MCR2
+
chan
,
info
->
regs
+
CR
);
sbus_writeb
((
AM_MUX_CHANNEL_B1
+
chan
)
|
(
AM_MUX_CHANNEL_Bb
<<
4
),
info
->
regs
+
DR
);
}
else
if
(
info
->
Bc
.
channel_status
==
CHANNEL_AVAILABLE
)
{
info
->
Bc
.
channel_status
=
CHANNEL_INUSE
;
info
->
Bc
.
xmit_idle_char
=
xmit_idle_char
;
info
->
Bisdn
[
chan
]
=
&
info
->
Bc
;
/* Multiplexor map - isdn (B1/2) to Bc */
sbus_writeb
(
AMR_MUX_MCR2
+
chan
,
info
->
regs
+
CR
);
sbus_writeb
((
AM_MUX_CHANNEL_B1
+
chan
)
|
(
AM_MUX_CHANNEL_Bc
<<
4
),
info
->
regs
+
DR
);
}
else
{
restore_flags
(
flags
);
return
(
-
1
);
}
/* Enable B channel transmit */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
tmp
=
sbus_readb
(
info
->
regs
+
DR
);
tmp
|=
AM_LIU_LMR1_B1_ENABL
+
chan
;
sbus_writeb
(
tmp
,
info
->
regs
+
DR
);
/* Enable B channel interrupts */
sbus_writeb
(
AMR_MUX_MCR4
,
info
->
regs
+
CR
);
sbus_writeb
(
AM_MUX_MCR4_ENABLE_INTS
|
AM_MUX_MCR4_REVERSE_Bb
|
AM_MUX_MCR4_REVERSE_Bc
,
info
->
regs
+
DR
);
restore_flags
(
flags
);
return
0
;
}
static
void
amd7930_bclose
(
int
dev
,
unsigned
int
chan
)
{
struct
amd7930_info
*
info
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
||
chan
<
0
||
chan
>
1
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
save_and_cli
(
flags
);
if
(
info
->
Bisdn
[
chan
])
{
u8
tmp
;
info
->
Bisdn
[
chan
]
->
channel_status
=
CHANNEL_AVAILABLE
;
sbus_writeb
(
AMR_MUX_MCR2
+
chan
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
info
->
Bisdn
[
chan
]
=
NULL
;
/* Disable B channel transmit */
sbus_writeb
(
AMR_LIU_LMR1
,
info
->
regs
+
CR
);
tmp
=
sbus_readb
(
info
->
regs
+
DR
);
tmp
&=
~
(
AM_LIU_LMR1_B1_ENABL
+
chan
);
sbus_writeb
(
tmp
,
info
->
regs
+
DR
);
if
(
info
->
Bb
.
channel_status
==
CHANNEL_AVAILABLE
&&
info
->
Bc
.
channel_status
==
CHANNEL_AVAILABLE
)
{
/* Disable B channel interrupts */
sbus_writeb
(
AMR_MUX_MCR4
,
info
->
regs
+
CR
);
sbus_writeb
(
0
,
info
->
regs
+
DR
);
}
}
restore_flags
(
flags
);
}
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
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
Bchan
=
info
->
Bisdn
[
chan
];
if
(
Bchan
)
{
save_and_cli
(
flags
);
Bchan
->
output_ptr
=
buffer
;
Bchan
->
output_count
=
count
;
Bchan
->
output_format
=
AUDIO_ENCODING_ULAW
;
Bchan
->
output_callback
=
(
void
*
)
callback
;
Bchan
->
output_callback_arg
=
callback_arg
;
restore_flags
(
flags
);
}
}
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
;
unsigned
long
flags
;
if
(
dev
>
num_drivers
)
return
;
info
=
(
struct
amd7930_info
*
)
drivers
[
dev
].
private
;
Bchan
=
info
->
Bisdn
[
chan
];
if
(
Bchan
)
{
save_and_cli
(
flags
);
Bchan
->
input_ptr
=
buffer
;
Bchan
->
input_count
=
size
;
Bchan
->
input_format
=
AUDIO_ENCODING_ULAW
;
Bchan
->
input_callback
=
(
void
*
)
callback
;
Bchan
->
input_callback_arg
=
callback_arg
;
restore_flags
(
flags
);
}
}
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
/*
* Device detection and initialization.
*/
static
struct
sparcaudio_operations
amd7930_ops
=
{
amd7930_open
,
amd7930_release
,
amd7930_ioctl
,
amd7930_start_output
,
amd7930_stop_output
,
amd7930_start_input
,
amd7930_stop_input
,
amd7930_sunaudio_getdev
,
amd7930_set_output_volume
,
amd7930_get_output_volume
,
amd7930_set_input_volume
,
amd7930_get_input_volume
,
amd7930_set_monitor_volume
,
amd7930_get_monitor_volume
,
NULL
,
/* amd7930_set_output_balance */
amd7930_get_output_balance
,
NULL
,
/* amd7930_set_input_balance */
amd7930_get_input_balance
,
amd7930_set_output_channels
,
amd7930_get_output_channels
,
amd7930_set_input_channels
,
amd7930_get_input_channels
,
amd7930_set_output_precision
,
amd7930_get_output_precision
,
amd7930_set_input_precision
,
amd7930_get_input_precision
,
amd7930_set_output_port
,
amd7930_get_output_port
,
NULL
,
/* amd7930_set_input_port */
amd7930_get_input_port
,
amd7930_set_encoding
,
amd7930_get_encoding
,
amd7930_set_encoding
,
amd7930_get_encoding
,
amd7930_set_output_rate
,
amd7930_get_output_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 */
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. */
static
int
amd7930_attach
(
struct
sparcaudio_driver
*
drv
,
int
node
,
struct
sbus_bus
*
sbus
,
struct
sbus_dev
*
sdev
)
{
struct
linux_prom_registers
regs
;
struct
linux_prom_irqs
irq
;
struct
resource
res
,
*
resp
;
struct
amd7930_info
*
info
;
int
err
;
/* Allocate our private information structure. */
drv
->
private
=
kmalloc
(
sizeof
(
struct
amd7930_info
),
GFP_KERNEL
);
if
(
drv
->
private
==
NULL
)
return
-
ENOMEM
;
/* Point at the information structure and initialize it. */
drv
->
ops
=
&
amd7930_ops
;
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
memset
(
info
,
0
,
sizeof
(
*
info
));
info
->
ints_on
=
1
;
/* force disable below */
drv
->
dev
=
sdev
;
/* Map the registers into memory. */
prom_getproperty
(
node
,
"reg"
,
(
char
*
)
&
regs
,
sizeof
(
regs
));
if
(
sbus
&&
sdev
)
{
resp
=
&
sdev
->
resource
[
0
];
}
else
{
resp
=
&
res
;
res
.
start
=
regs
.
phys_addr
;
res
.
end
=
res
.
start
+
regs
.
reg_size
-
1
;
res
.
flags
=
IORESOURCE_IO
|
(
regs
.
which_io
&
0xff
);
}
info
->
regs_size
=
regs
.
reg_size
;
info
->
regs
=
sbus_ioremap
(
resp
,
0
,
regs
.
reg_size
,
"amd7930"
);
if
(
!
info
->
regs
)
{
printk
(
KERN_ERR
"amd7930: could not remap registers
\n
"
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
/* Put amd7930 in idle mode (interrupts disabled) */
amd7930_idle
(
info
);
/* Enable extended FIFO operation on D-channel */
sbus_writeb
(
AMR_DLC_EFCR
,
info
->
regs
+
CR
);
sbus_writeb
(
AMR_DLC_EFCR_EXTEND_FIFO
,
info
->
regs
+
DR
);
sbus_writeb
(
AMR_DLC_DMR4
,
info
->
regs
+
CR
);
sbus_writeb
(
/* AMR_DLC_DMR4_RCV_30 | */
AMR_DLC_DMR4_XMT_14
,
info
->
regs
+
DR
);
/* Attach the interrupt handler to the audio interrupt. */
prom_getproperty
(
node
,
"intr"
,
(
char
*
)
&
irq
,
sizeof
(
irq
));
info
->
irq
=
irq
.
pri
;
request_irq
(
info
->
irq
,
amd7930_interrupt
,
SA_INTERRUPT
,
"amd7930"
,
drv
);
amd7930_enable_ints
(
info
);
/* Initalize the local copy of the MAP registers. */
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
;
info
->
format_type
=
AUDIO_ENCODING_ULAW
;
info
->
Bb
.
input_format
=
AUDIO_ENCODING_ULAW
;
info
->
Bb
.
output_format
=
AUDIO_ENCODING_ULAW
;
info
->
Bc
.
input_format
=
AUDIO_ENCODING_ULAW
;
info
->
Bc
.
output_format
=
AUDIO_ENCODING_ULAW
;
amd7930_update_map
(
drv
);
/* Register the amd7930 with the midlevel audio driver. */
err
=
register_sparcaudio_driver
(
drv
,
1
);
if
(
err
<
0
)
{
printk
(
KERN_ERR
"amd7930: unable to register
\n
"
);
free_irq
(
info
->
irq
,
drv
);
sbus_iounmap
(
info
->
regs
,
info
->
regs_size
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
/* Announce the hardware to the user. */
printk
(
KERN_INFO
"amd7930 at %lx irq %d
\n
"
,
info
->
regs
,
info
->
irq
);
/* Success! */
return
0
;
}
/* Detach from an amd7930 chip given the device structure. */
static
void
__exit
amd7930_detach
(
struct
sparcaudio_driver
*
drv
)
{
struct
amd7930_info
*
info
=
(
struct
amd7930_info
*
)
drv
->
private
;
unregister_sparcaudio_driver
(
drv
,
1
);
amd7930_idle
(
info
);
free_irq
(
info
->
irq
,
drv
);
sbus_iounmap
(
info
->
regs
,
info
->
regs_size
);
kfree
(
drv
->
private
);
}
/* Probe for the amd7930 chip and then attach the driver. */
static
int
__init
amd7930_init
(
void
)
{
struct
sbus_bus
*
sbus
;
struct
sbus_dev
*
sdev
;
int
node
;
/* Try to find the sun4c "audio" node first. */
node
=
prom_getchild
(
prom_root_node
);
node
=
prom_searchsiblings
(
node
,
"audio"
);
if
(
node
&&
amd7930_attach
(
&
drivers
[
0
],
node
,
NULL
,
NULL
)
==
0
)
num_drivers
=
1
;
else
num_drivers
=
0
;
/* Probe each SBUS for amd7930 chips. */
for_all_sbusdev
(
sdev
,
sbus
)
{
if
(
!
strcmp
(
sdev
->
prom_name
,
"audio"
))
{
/* Don't go over the max number of drivers. */
if
(
num_drivers
>=
MAX_DRIVERS
)
continue
;
if
(
amd7930_attach
(
&
drivers
[
num_drivers
],
sdev
->
prom_node
,
sdev
->
bus
,
sdev
)
==
0
)
num_drivers
++
;
}
}
/* Only return success if we found some amd7930 chips. */
return
(
num_drivers
>
0
)
?
0
:
-
EIO
;
}
static
void
__exit
amd7930_exit
(
void
)
{
register
int
i
;
for
(
i
=
0
;
i
<
num_drivers
;
i
++
)
{
amd7930_detach
(
&
drivers
[
i
]);
num_drivers
--
;
}
}
module_init
(
amd7930_init
);
module_exit
(
amd7930_exit
);
MODULE_LICENSE
(
"GPL"
);
/*************************************************************/
/* Audio format conversion */
/*************************************************************/
/* Translation tables */
static
unsigned
char
ulaw
[]
=
{
3
,
7
,
11
,
15
,
19
,
23
,
27
,
31
,
35
,
39
,
43
,
47
,
51
,
55
,
59
,
63
,
66
,
68
,
70
,
72
,
74
,
76
,
78
,
80
,
82
,
84
,
86
,
88
,
90
,
92
,
94
,
96
,
98
,
99
,
100
,
101
,
102
,
103
,
104
,
105
,
106
,
107
,
108
,
109
,
110
,
111
,
112
,
113
,
113
,
114
,
114
,
115
,
115
,
116
,
116
,
117
,
117
,
118
,
118
,
119
,
119
,
120
,
120
,
121
,
121
,
121
,
122
,
122
,
122
,
122
,
123
,
123
,
123
,
123
,
124
,
124
,
124
,
124
,
125
,
125
,
125
,
125
,
125
,
125
,
126
,
126
,
126
,
126
,
126
,
126
,
126
,
126
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
127
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
253
,
249
,
245
,
241
,
237
,
233
,
229
,
225
,
221
,
217
,
213
,
209
,
205
,
201
,
197
,
193
,
190
,
188
,
186
,
184
,
182
,
180
,
178
,
176
,
174
,
172
,
170
,
168
,
166
,
164
,
162
,
160
,
158
,
157
,
156
,
155
,
154
,
153
,
152
,
151
,
150
,
149
,
148
,
147
,
146
,
145
,
144
,
143
,
143
,
142
,
142
,
141
,
141
,
140
,
140
,
139
,
139
,
138
,
138
,
137
,
137
,
136
,
136
,
135
,
135
,
135
,
134
,
134
,
134
,
134
,
133
,
133
,
133
,
133
,
132
,
132
,
132
,
132
,
131
,
131
,
131
,
131
,
131
,
131
,
130
,
130
,
130
,
130
,
130
,
130
,
130
,
130
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
129
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
,
128
};
static
__u8
mulaw2bilinear
(
__u8
data
)
{
return
ulaw
[
data
];
}
static
unsigned
char
linear
[]
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
2
,
0
,
0
,
0
,
3
,
0
,
0
,
0
,
4
,
0
,
0
,
0
,
5
,
0
,
0
,
0
,
6
,
0
,
0
,
0
,
7
,
0
,
0
,
0
,
8
,
0
,
0
,
0
,
9
,
0
,
0
,
0
,
10
,
0
,
0
,
0
,
11
,
0
,
0
,
0
,
12
,
0
,
0
,
0
,
13
,
0
,
0
,
0
,
14
,
0
,
0
,
0
,
15
,
0
,
0
,
16
,
0
,
17
,
0
,
18
,
0
,
19
,
0
,
20
,
0
,
21
,
0
,
22
,
0
,
23
,
0
,
24
,
0
,
25
,
0
,
26
,
0
,
27
,
0
,
28
,
0
,
29
,
0
,
30
,
0
,
31
,
0
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
48
,
50
,
52
,
54
,
56
,
58
,
60
,
62
,
65
,
69
,
73
,
77
,
83
,
91
,
103
,
255
,
231
,
219
,
211
,
205
,
201
,
197
,
193
,
190
,
188
,
186
,
184
,
182
,
180
,
178
,
176
,
174
,
173
,
172
,
171
,
170
,
169
,
168
,
167
,
166
,
165
,
164
,
163
,
162
,
161
,
160
,
0
,
159
,
0
,
158
,
0
,
157
,
0
,
156
,
0
,
155
,
0
,
154
,
0
,
153
,
0
,
152
,
0
,
151
,
0
,
150
,
0
,
149
,
0
,
148
,
0
,
147
,
0
,
146
,
0
,
145
,
0
,
144
,
0
,
0
,
143
,
0
,
0
,
0
,
142
,
0
,
0
,
0
,
141
,
0
,
0
,
0
,
140
,
0
,
0
,
0
,
139
,
0
,
0
,
0
,
138
,
0
,
0
,
0
,
137
,
0
,
0
,
0
,
136
,
0
,
0
,
0
,
135
,
0
,
0
,
0
,
134
,
0
,
0
,
0
,
133
,
0
,
0
,
0
,
132
,
0
,
0
,
0
,
131
,
0
,
0
,
0
,
130
,
0
,
0
,
0
,
129
,
0
,
0
,
0
,
128
,
0
,
0
};
static
__u8
bilinear2mulaw
(
__u8
data
)
{
return
linear
[
data
];
}
static
int
exp_lut
[
256
]
=
{
0
,
0
,
1
,
1
,
2
,
2
,
2
,
2
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
3
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
};
#define BIAS 0x84
#define CLIP 32635
#define SWAP_ENDIAN(x) ((x >> 8) | ((x & 0xff) << 8))
static
__u8
linear2mulaw
(
__u16
data
)
{
static
int
sign
,
exponent
,
mantissa
;
/* not really sure, if swapping is ok - comment next line to disable it */
data
=
SWAP_ENDIAN
(
data
);
sign
=
(
data
>>
8
)
&
0x80
;
if
(
sign
!=
0
)
data
=
-
data
;
if
(
data
>
CLIP
)
data
=
CLIP
;
data
+=
BIAS
;
exponent
=
exp_lut
[(
data
>>
7
)
&
0xFF
];
mantissa
=
(
data
>>
(
exponent
+
3
))
&
0x0F
;
return
(
~
(
sign
|
(
exponent
<<
4
)
|
mantissa
));
}
static
__u16
mulaw2linear
(
__u8
data
)
{
/* this conversion is not working */
return
data
;
}
#if 0
#define INOUT(x,y) (((x) << 16) | (y))
static int convert_audio(int in_format, int out_format, __u8* buffer, int count)
{
static int i,sign,exponent;
static __u16 data;
if (in_format == out_format) return count;
switch(INOUT(in_format, out_format)) {
case INOUT(AUDIO_ENCODING_ULAW, AUDIO_ENCODING_LINEAR8):
for (i = 0;i < count; i++) {
buffer[i] = ulaw[buffer[i]];
};
break;
case INOUT(AUDIO_ENCODING_ULAW, AUDIO_ENCODING_LINEAR):
break;
case INOUT(AUDIO_ENCODING_LINEAR, AUDIO_ENCODING_ULAW):
/* buffer is two-byte => convert to first */
for (i = 0; i < count/2; i++) {
data = ((__u16*)buffer)[i];
sign = (data >> 8) & 0x80;
if (data > CLIP) data = CLIP;
data += BIAS;
exponent = exp_lut[(data >> 7) & 0xFF];
buffer[i] = ~(sign | (exponent << 4) |
((data >> (exponent + 3)) & 0x0F));
};
break;
case INOUT(AUDIO_ENCODING_LINEAR8, AUDIO_ENCODING_ULAW):
for (i = 0; i < count; i++) {
buffer[i] = linear[buffer[i]];
};
break;
default:
return 0;
};
return count;
}
#undef INOUT
#endif
#undef BIAS
#undef CLIP
#undef SWAP_ENDIAN
drivers/sbus/audio/amd7930.h
deleted
100644 → 0
View file @
2ee094ef
/* $Id: amd7930.h,v 1.8 1999/09/21 14:37:10 davem Exp $
* drivers/sbus/audio/amd7930.h
*
* Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
*
* Definitions for the AMD79C30 Digital Subscriber Controller which is
* used as an audio chip in sun4c architecture machines. The
* information in this file is based on Advanced Micro Devices
* Publication 09893, Rev G, Amendment /0, Final (a.k.a. the data
* sheet).
*/
#ifndef _AMD7930_H_
#define _AMD7930_H_
#include <linux/types.h>
#include <linux/version.h>
/* Register interface presented to the CPU by the amd7930. */
#define CR 0x00UL
/* Command Register (W) */
#define IR CR
/* Interrupt Register (R) */
#define DR 0x01UL
/* Data Register (R/W) */
#define DSR1 0x02UL
/* D-channel Status Register 1 (R) */
#define DER 0x03UL
/* D-channel Error Register (R) */
#define DCTB 0x04UL
/* D-channel Transmit Buffer (W) */
#define DCRB DCTB
/* D-channel Receive Buffer (R) */
#define BBTB 0x05UL
/* Bb-channel Transmit Buffer (W) */
#define BBRB BBTB
/* Bb-channel Receive Buffer (R) */
#define BCTB 0x06UL
/* Bc-channel Transmit Buffer (W) */
#define BCRB BCTB
/* Bc-channel Receive Buffer (R) */
#define DSR2 0x07UL
/* D-channel Status Register 2 (R) */
/* Indirect registers in the Main Audio Processor. */
struct
amd7930_map
{
__u16
x
[
8
];
__u16
r
[
8
];
__u16
gx
;
__u16
gr
;
__u16
ger
;
__u16
stgr
;
__u16
ftgr
;
__u16
atgr
;
__u8
mmr1
;
__u8
mmr2
;
};
/* After an amd7930 interrupt, reading the Interrupt Register (ir)
* clears the interrupt and returns a bitmask indicated which
* interrupt source(s) require service
*/
#define AMR_IR_DTTHRSH 0x01
/* D-channel xmit threshold */
#define AMR_IR_DRTHRSH 0x02
/* D-channel recv threshold */
#define AMR_IR_DSRI 0x04
/* D-channel packet status */
#define AMR_IR_DERI 0x08
/* D-channel error */
#define AMR_IR_BBUF 0x10
/* B-channel data xfer */
#define AMR_IR_LSRI 0x20
/* LIU status */
#define AMR_IR_DSR2I 0x40
/* D-channel buffer status */
#define AMR_IR_MLTFRMI 0x80
/* multiframe or PP */
/* The amd7930 has "indirect registers" which are accessed by writing
* the register number into the Command Register and then reading or
* writing values from the Data Register as appropriate. We define the
* AMR_* macros to be the indirect register numbers and AM_* macros to
* be bits in whatever register is referred to.
*/
/* Initialization */
#define AMR_INIT 0x21
#define AM_INIT_ACTIVE 0x01
#define AM_INIT_DATAONLY 0x02
#define AM_INIT_POWERDOWN 0x03
#define AM_INIT_DISABLE_INTS 0x04
#define AMR_INIT2 0x20
#define AM_INIT2_ENABLE_POWERDOWN 0x20
#define AM_INIT2_ENABLE_MULTIFRAME 0x10
/* Line Interface Unit */
#define AMR_LIU_LSR 0xA1
#define AM_LIU_LSR_STATE 0x07
#define AM_LIU_LSR_F3 0x08
#define AM_LIU_LSR_F7 0x10
#define AM_LIU_LSR_F8 0x20
#define AM_LIU_LSR_HSW 0x40
#define AM_LIU_LSR_HSW_CHG 0x80
#define AMR_LIU_LPR 0xA2
#define AMR_LIU_LMR1 0xA3
#define AM_LIU_LMR1_B1_ENABL 0x01
#define AM_LIU_LMR1_B2_ENABL 0x02
#define AM_LIU_LMR1_F_DISABL 0x04
#define AM_LIU_LMR1_FA_DISABL 0x08
#define AM_LIU_LMR1_REQ_ACTIV 0x10
#define AM_LIU_LMR1_F8_F3 0x20
#define AM_LIU_LMR1_LIU_ENABL 0x40
#define AMR_LIU_LMR2 0xA4
#define AM_LIU_LMR2_DECHO 0x01
#define AM_LIU_LMR2_DLOOP 0x02
#define AM_LIU_LMR2_DBACKOFF 0x04
#define AM_LIU_LMR2_EN_F3_INT 0x08
#define AM_LIU_LMR2_EN_F8_INT 0x10
#define AM_LIU_LMR2_EN_HSW_INT 0x20
#define AM_LIU_LMR2_EN_F7_INT 0x40
#define AMR_LIU_2_4 0xA5
#define AMR_LIU_MF 0xA6
#define AMR_LIU_MFSB 0xA7
#define AMR_LIU_MFQB 0xA8
/* Multiplexor */
#define AMR_MUX_MCR1 0x41
#define AMR_MUX_MCR2 0x42
#define AMR_MUX_MCR3 0x43
#define AM_MUX_CHANNEL_B1 0x01
#define AM_MUX_CHANNEL_B2 0x02
#define AM_MUX_CHANNEL_Ba 0x03
#define AM_MUX_CHANNEL_Bb 0x04
#define AM_MUX_CHANNEL_Bc 0x05
#define AM_MUX_CHANNEL_Bd 0x06
#define AM_MUX_CHANNEL_Be 0x07
#define AM_MUX_CHANNEL_Bf 0x08
#define AMR_MUX_MCR4 0x44
#define AM_MUX_MCR4_ENABLE_INTS 0x08
#define AM_MUX_MCR4_REVERSE_Bb 0x10
#define AM_MUX_MCR4_REVERSE_Bc 0x20
#define AMR_MUX_1_4 0x45
/* Main Audio Processor */
#define AMR_MAP_X 0x61
#define AMR_MAP_R 0x62
#define AMR_MAP_GX 0x63
#define AMR_MAP_GR 0x64
#define AMR_MAP_GER 0x65
#define AMR_MAP_STGR 0x66
#define AMR_MAP_FTGR_1_2 0x67
#define AMR_MAP_ATGR_1_2 0x68
#define AMR_MAP_MMR1 0x69
#define AM_MAP_MMR1_ALAW 0x01
#define AM_MAP_MMR1_GX 0x02
#define AM_MAP_MMR1_GR 0x04
#define AM_MAP_MMR1_GER 0x08
#define AM_MAP_MMR1_X 0x10
#define AM_MAP_MMR1_R 0x20
#define AM_MAP_MMR1_STG 0x40
#define AM_MAP_MMR1_LOOPBACK 0x80
#define AMR_MAP_MMR2 0x6A
#define AM_MAP_MMR2_AINB 0x01
#define AM_MAP_MMR2_LS 0x02
#define AM_MAP_MMR2_ENABLE_DTMF 0x04
#define AM_MAP_MMR2_ENABLE_TONEGEN 0x08
#define AM_MAP_MMR2_ENABLE_TONERING 0x10
#define AM_MAP_MMR2_DISABLE_HIGHPASS 0x20
#define AM_MAP_MMR2_DISABLE_AUTOZERO 0x40
#define AMR_MAP_1_10 0x6B
#define AMR_MAP_MMR3 0x6C
#define AMR_MAP_STRA 0x6D
#define AMR_MAP_STRF 0x6E
#define AMR_MAP_PEAKX 0x70
#define AMR_MAP_PEAKR 0x71
#define AMR_MAP_15_16 0x72
/* Data Link Controller */
#define AMR_DLC_FRAR_1_2_3 0x81
#define AMR_DLC_SRAR_1_2_3 0x82
#define AMR_DLC_TAR 0x83
#define AMR_DLC_DRLR 0x84
#define AMR_DLC_DTCR 0x85
#define AMR_DLC_DMR1 0x86
#define AMR_DLC_DMR1_DTTHRSH_INT 0x01
#define AMR_DLC_DMR1_DRTHRSH_INT 0x02
#define AMR_DLC_DMR1_TAR_ENABL 0x04
#define AMR_DLC_DMR1_EORP_INT 0x08
#define AMR_DLC_DMR1_EN_ADDR1 0x10
#define AMR_DLC_DMR1_EN_ADDR2 0x20
#define AMR_DLC_DMR1_EN_ADDR3 0x40
#define AMR_DLC_DMR1_EN_ADDR4 0x80
#define AMR_DLC_DMR1_EN_ADDRS 0xf0
#define AMR_DLC_DMR2 0x87
#define AMR_DLC_DMR2_RABRT_INT 0x01
#define AMR_DLC_DMR2_RESID_INT 0x02
#define AMR_DLC_DMR2_COLL_INT 0x04
#define AMR_DLC_DMR2_FCS_INT 0x08
#define AMR_DLC_DMR2_OVFL_INT 0x10
#define AMR_DLC_DMR2_UNFL_INT 0x20
#define AMR_DLC_DMR2_OVRN_INT 0x40
#define AMR_DLC_DMR2_UNRN_INT 0x80
#define AMR_DLC_1_7 0x88
#define AMR_DLC_DRCR 0x89
#define AMR_DLC_RNGR1 0x8A
#define AMR_DLC_RNGR2 0x8B
#define AMR_DLC_FRAR4 0x8C
#define AMR_DLC_SRAR4 0x8D
#define AMR_DLC_DMR3 0x8E
#define AMR_DLC_DMR3_VA_INT 0x01
#define AMR_DLC_DMR3_EOTP_INT 0x02
#define AMR_DLC_DMR3_LBRP_INT 0x04
#define AMR_DLC_DMR3_RBA_INT 0x08
#define AMR_DLC_DMR3_LBT_INT 0x10
#define AMR_DLC_DMR3_TBE_INT 0x20
#define AMR_DLC_DMR3_RPLOST_INT 0x40
#define AMR_DLC_DMR3_KEEP_FCS 0x80
#define AMR_DLC_DMR4 0x8F
#define AMR_DLC_DMR4_RCV_1 0x00
#define AMR_DLC_DMR4_RCV_2 0x01
#define AMR_DLC_DMR4_RCV_4 0x02
#define AMR_DLC_DMR4_RCV_8 0x03
#define AMR_DLC_DMR4_RCV_16 0x01
#define AMR_DLC_DMR4_RCV_24 0x02
#define AMR_DLC_DMR4_RCV_30 0x03
#define AMR_DLC_DMR4_XMT_1 0x00
#define AMR_DLC_DMR4_XMT_2 0x04
#define AMR_DLC_DMR4_XMT_4 0x08
#define AMR_DLC_DMR4_XMT_8 0x0c
#define AMR_DLC_DMR4_XMT_10 0x08
#define AMR_DLC_DMR4_XMT_14 0x0c
#define AMR_DLC_DMR4_IDLE_MARK 0x00
#define AMR_DLC_DMR4_IDLE_FLAG 0x10
#define AMR_DLC_DMR4_ADDR_BOTH 0x00
#define AMR_DLC_DMR4_ADDR_1ST 0x20
#define AMR_DLC_DMR4_ADDR_2ND 0xa0
#define AMR_DLC_DMR4_CR_ENABLE 0x40
#define AMR_DLC_12_15 0x90
#define AMR_DLC_ASR 0x91
#define AMR_DLC_EFCR 0x92
#define AMR_DLC_EFCR_EXTEND_FIFO 0x01
#define AMR_DLC_EFCR_SEC_PKT_INT 0x02
#define AMR_DSR1_VADDR 0x01
#define AMR_DSR1_EORP 0x02
#define AMR_DSR1_PKT_IP 0x04
#define AMR_DSR1_DECHO_ON 0x08
#define AMR_DSR1_DLOOP_ON 0x10
#define AMR_DSR1_DBACK_OFF 0x20
#define AMR_DSR1_EOTP 0x40
#define AMR_DSR1_CXMT_ABRT 0x80
#define AMR_DSR2_LBRP 0x01
#define AMR_DSR2_RBA 0x02
#define AMR_DSR2_RPLOST 0x04
#define AMR_DSR2_LAST_BYTE 0x08
#define AMR_DSR2_TBE 0x10
#define AMR_DSR2_MARK_IDLE 0x20
#define AMR_DSR2_FLAG_IDLE 0x40
#define AMR_DSR2_SECOND_PKT 0x80
#define AMR_DER_RABRT 0x01
#define AMR_DER_RFRAME 0x02
#define AMR_DER_COLLISION 0x04
#define AMR_DER_FCS 0x08
#define AMR_DER_OVFL 0x10
#define AMR_DER_UNFL 0x20
#define AMR_DER_OVRN 0x40
#define AMR_DER_UNRN 0x80
/* Peripheral Port */
#define AMR_PP_PPCR1 0xC0
#define AMR_PP_PPSR 0xC1
#define AMR_PP_PPIER 0xC2
#define AMR_PP_MTDR 0xC3
#define AMR_PP_MRDR 0xC3
#define AMR_PP_CITDR0 0xC4
#define AMR_PP_CIRDR0 0xC4
#define AMR_PP_CITDR1 0xC5
#define AMR_PP_CIRDR1 0xC5
#define AMR_PP_PPCR2 0xC8
#define AMR_PP_PPCR3 0xC9
/* Give this chip a "default" sample rate */
#define AMD7930_RATE (8000)
#endif
/* _AMD7930_H_ */
drivers/sbus/audio/audio.c
deleted
100644 → 0
View file @
2ee094ef
This source diff could not be displayed because it is too large. You can
view the blob
instead.
drivers/sbus/audio/cs4215.h
deleted
100644 → 0
View file @
2ee094ef
/* $Id: cs4215.h,v 1.8 2000/10/27 07:01:38 uzi Exp $
* drivers/sbus/audio/cs4215.h
*
* Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
* Used with dbri.h
*/
#ifndef _CS4215_H_
#define _CS4215_H_
struct
cs4215
{
__u8
data
[
4
];
/* Data mode: Time slots 5-8 */
__u8
ctrl
[
4
];
/* Ctrl mode: Time slots 1-4 */
__u8
onboard
;
__u8
offset
;
/* Bit offset from frame sync to time slot 1 */
volatile
__u32
status
;
volatile
__u32
version
;
};
/*
* Control mode first
*/
/* Time Slot 1, Status register */
#define CS4215_CLB (1<<2)
/* Control Latch Bit */
#define CS4215_OLB (1<<3)
/* 1: line: 2.0V, speaker 4V */
/* 0: line: 2.8V, speaker 8V */
#define CS4215_MLB (1<<4)
/* 1: Microphone: 20dB gain disabled */
#define CS4215_RSRVD_1 (1<<5)
/* Time Slot 2, Data Format Register */
#define CS4215_DFR_LINEAR16 0
#define CS4215_DFR_ULAW 1
#define CS4215_DFR_ALAW 2
#define CS4215_DFR_LINEAR8 3
#define CS4215_DFR_STEREO (1<<2)
static
struct
{
unsigned
short
freq
;
unsigned
char
xtal
;
unsigned
char
csval
;
}
CS4215_FREQ
[]
=
{
{
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 */
#define CS4215_12_MASK 0xfcbf
/* Mask off reserved bits in slot 1 & 2 */
/* Time Slot 3, Serial Port Control register */
#define CS4215_XEN (1<<0)
/* 0: Enable serial output */
#define CS4215_XCLK (1<<1)
/* 1: Master mode: Generate SCLK */
#define CS4215_BSEL_64 (0<<2)
/* Bitrate: 64 bits per frame */
#define CS4215_BSEL_128 (1<<2)
#define CS4215_BSEL_256 (2<<2)
#define CS4215_MCK_MAST (0<<4)
/* Master clock */
#define CS4215_MCK_XTL1 (1<<4)
/* 24.576 MHz clock source */
#define CS4215_MCK_XTL2 (2<<4)
/* 16.9344 MHz clock source */
#define CS4215_MCK_CLK1 (3<<4)
/* Clockin, 256 x Fs */
#define CS4215_MCK_CLK2 (4<<4)
/* Clockin, see DFR */
/* Time Slot 4, Test Register */
#define CS4215_DAD (1<<0)
/* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */
#define CS4215_ENL (1<<1)
/* Enable Loopback Testing */
/* Time Slot 5, Parallel Port Register */
/* Read only here and the same as the in data mode */
/* Time Slot 6, Reserved */
/* Time Slot 7, Version Register */
#define CS4215_VERSION_MASK 0xf
/* Known versions 0/C, 1/D, 2/E */
/* Time Slot 8, Reserved */
/*
* Data mode
*/
/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data */
/* Time Slot 5, Output Setting */
#define CS4215_LO(v) v
/* Left Output Attenuation 0x3f: -94.5 dB */
#define CS4215_LE (1<<6)
/* Line Out Enable */
#define CS4215_HE (1<<7)
/* Headphone Enable */
/* Time Slot 6, Output Setting */
#define CS4215_RO(v) v
/* Right Output Attenuation 0x3f: -94.5 dB */
#define CS4215_SE (1<<6)
/* Speaker Enable */
#define CS4215_ADI (1<<7)
/* A/D Data Invalid: Busy in calibration */
/* Time Slot 7, Input Setting */
#define CS4215_LG(v) v
/* Left Gain Setting 0xf: 22.5 dB */
#define CS4215_IS (1<<4)
/* Input Select: 1=Microphone, 0=Line */
#define CS4215_OVR (1<<5)
/* 1: Overrange condition occurred */
#define CS4215_PIO0 (1<<6)
/* Parallel I/O 0 */
#define CS4215_PIO1 (1<<7)
/* Time Slot 8, Input Setting */
#define CS4215_RG(v) v
/* Right Gain Setting 0xf: 22.5 dB */
#define CS4215_MA(v) (v<<4)
/* Monitor Path Attenuation 0xf: mute */
#endif
/* _CS4215_H_ */
drivers/sbus/audio/cs4231.c
deleted
100644 → 0
View file @
2ee094ef
/* $Id: cs4231.c,v 1.47 2001/10/08 22:19:50 davem Exp $
* drivers/sbus/audio/cs4231.c
*
* Copyright 1996, 1997, 1998, 1999 Derrick J Brashear (shadow@andrew.cmu.edu)
* The 4231/ebus support was written by David Miller, who didn't bother
* crediting himself here, so I will.
*
* Based on the AMD7930 driver:
* 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.
*
* This was culled from the Crystal docs on the 4231a, and the addendum they
* faxed me on the 4231.
* The APC DMA controller support unfortunately is not documented. Thanks, Sun.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/soundcard.h>
#include <linux/version.h>
#include <linux/ioport.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/sbus.h>
#ifdef CONFIG_PCI
#define EB4231_SUPPORT
#include <asm/ebus.h>
#include <asm/pbm.h>
#endif
#include <asm/audioio.h>
#include "cs4231.h"
#undef __CS4231_DEBUG
#undef __CS4231_TRACE
#define __CS4231_ERROR
#ifdef __CS4231_ERROR
#define eprintk(x) printk x
#else
#define eprintk(x)
#endif
#ifdef __CS4231_TRACE
#define tprintk(x) printk x
#else
#define tprintk(x)
#endif
#ifdef __CS4231_DEBUG
#define dprintk(x) printk x
#else
#define dprintk(x)
#endif
#define MAX_DRIVERS 1
static
struct
sparcaudio_driver
drivers
[
MAX_DRIVERS
];
static
int
num_drivers
;
static
int
cs4231_record_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
);
static
int
cs4231_play_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
);
static
void
cs4231_ready
(
struct
sparcaudio_driver
*
drv
);
static
void
cs4231_playintr
(
struct
sparcaudio_driver
*
drv
,
int
);
static
int
cs4231_recintr
(
struct
sparcaudio_driver
*
drv
);
static
int
cs4231_output_muted
(
struct
sparcaudio_driver
*
drv
,
int
value
);
static
void
cs4231_pollinput
(
struct
sparcaudio_driver
*
drv
);
static
int
cs4231_length_to_samplecount
(
struct
audio_prinfo
*
thisdir
,
unsigned
int
length
);
static
void
cs4231_getsamplecount
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
length
,
unsigned
int
value
);
#ifdef EB4231_SUPPORT
static
void
eb4231_pollinput
(
struct
sparcaudio_driver
*
drv
);
#endif
/* Serveral shorthands save typing... */
#define CHIP_READY() \
do { udelay(100); cs4231_ready(drv); udelay(1000); } while(0)
#define WRITE_IAR(__VAL) \
CS4231_WRITE8(cs4231_chip, cs4231_chip->regs + IAR, __VAL)
#define WRITE_IDR(__VAL) \
CS4231_WRITE8(cs4231_chip, cs4231_chip->regs + IDR, __VAL)
#define READ_IAR() \
CS4231_READ8(cs4231_chip, cs4231_chip->regs + IAR)
#define READ_IDR() \
CS4231_READ8(cs4231_chip, cs4231_chip->regs + IDR)
/* Enable cs4231 interrupts atomically. */
static
void
cs4231_enable_interrupts
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"enabling interrupts
\n
"
));
save_flags
(
flags
);
cli
();
if
((
cs4231_chip
->
status
&
CS_STATUS_INTS_ON
)
==
0
)
{
WRITE_IAR
(
0xa
);
WRITE_IDR
(
INTR_ON
);
cs4231_chip
->
status
|=
CS_STATUS_INTS_ON
;
}
restore_flags
(
flags
);
}
/* Disable cs4231 interrupts atomically. */
static
void
cs4231_disable_interrupts
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"disabling interrupts
\n
"
));
save_flags
(
flags
);
cli
();
if
((
cs4231_chip
->
status
&
CS_STATUS_INTS_ON
)
!=
0
)
{
WRITE_IAR
(
0xa
);
WRITE_IDR
(
INTR_OFF
);
cs4231_chip
->
status
&=
~
CS_STATUS_INTS_ON
;
}
restore_flags
(
flags
);
}
static
void
cs4231_enable_play
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"enabling play
\n
"
));
save_flags
(
flags
);
cli
();
WRITE_IAR
(
0x9
);
WRITE_IDR
(
READ_IDR
()
|
PEN_ENABLE
);
restore_flags
(
flags
);
}
static
void
cs4231_disable_play
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"disabling play
\n
"
));
save_flags
(
flags
);
cli
();
WRITE_IAR
(
0x9
);
WRITE_IDR
(
READ_IDR
()
&
PEN_DISABLE
);
restore_flags
(
flags
);
}
static
void
cs4231_enable_rec
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"enabling rec
\n
"
));
save_flags
(
flags
);
cli
();
WRITE_IAR
(
0x9
);
WRITE_IDR
(
READ_IDR
()
|
CEN_ENABLE
);
restore_flags
(
flags
);
}
static
void
cs4231_disable_rec
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
long
flags
;
tprintk
((
"disabling rec
\n
"
));
save_flags
(
flags
);
cli
();
WRITE_IAR
(
0x9
);
WRITE_IDR
(
READ_IDR
()
&
CEN_DISABLE
);
restore_flags
(
flags
);
}
static
struct
cs4231_rates
{
int
speed
,
bits
;
}
cs4231_rate_table
[]
=
{
{
5512
,
CS4231_DFR_5512
},
{
6615
,
CS4231_DFR_6615
},
{
8000
,
CS4231_DFR_8000
},
{
9600
,
CS4231_DFR_9600
},
{
11025
,
CS4231_DFR_11025
},
{
16000
,
CS4231_DFR_16000
},
{
18900
,
CS4231_DFR_18900
},
{
22050
,
CS4231_DFR_22050
},
{
27429
,
CS4231_DFR_27429
},
{
32000
,
CS4231_DFR_32000
},
{
33075
,
CS4231_DFR_33075
},
{
37800
,
CS4231_DFR_37800
},
{
44100
,
CS4231_DFR_44100
},
{
48000
,
CS4231_DFR_48000
}
};
#define NUM_RATES (sizeof(cs4231_rate_table) / sizeof(struct cs4231_rates))
static
int
cs4231_rate_to_bits
(
struct
sparcaudio_driver
*
drv
,
int
*
value
)
{
struct
cs4231_rates
*
p
=
&
cs4231_rate_table
[
0
];
int
i
,
wanted
=
*
value
;
/* We try to be nice and approximate what the user asks for. */
if
(
wanted
<
5512
)
wanted
=
5512
;
if
(
wanted
>
48000
)
wanted
=
48000
;
for
(
i
=
0
;
i
<
NUM_RATES
;
i
++
,
p
++
)
{
/* Exact match? */
if
(
wanted
==
p
->
speed
)
break
;
/* If we're inbetween two entries, and neither is exact,
* pick the closest one.
*/
if
(
wanted
==
p
[
1
].
speed
)
continue
;
if
(
wanted
>
p
->
speed
&&
wanted
<
p
[
1
].
speed
)
{
int
diff1
,
diff2
;
diff1
=
wanted
-
p
->
speed
;
diff2
=
p
[
1
].
speed
-
wanted
;
if
(
diff2
<
diff1
)
p
++
;
break
;
}
}
*
value
=
p
->
speed
;
return
p
->
bits
;
}
static
int
cs4231_encoding_to_bits
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
int
set_bits
;
switch
(
value
)
{
case
AUDIO_ENCODING_ULAW
:
set_bits
=
CS4231_DFR_ULAW
;
break
;
case
AUDIO_ENCODING_ALAW
:
set_bits
=
CS4231_DFR_ALAW
;
break
;
case
AUDIO_ENCODING_DVI
:
set_bits
=
CS4231_DFR_ADPCM
;
break
;
case
AUDIO_ENCODING_LINEARLE
:
set_bits
=
CS4231_DFR_LINEARLE
;
break
;
case
AUDIO_ENCODING_LINEAR
:
set_bits
=
CS4231_DFR_LINEARBE
;
break
;
case
AUDIO_ENCODING_LINEAR8
:
set_bits
=
CS4231_DFR_LINEAR8
;
break
;
default:
set_bits
=
-
EINVAL
;
break
;
};
return
set_bits
;
}
static
int
cs4231_set_output_encoding
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
,
set_bits
;
tprintk
((
"output encoding %d
\n
"
,
value
));
if
(
value
!=
0
)
{
set_bits
=
cs4231_encoding_to_bits
(
drv
,
value
);
if
(
set_bits
>=
0
)
{
READ_IDR
();
READ_IDR
();
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x8
);
tmp_bits
=
READ_IDR
();
WRITE_IDR
(
CHANGE_ENCODING
(
tmp_bits
,
set_bits
));
READ_IDR
();
READ_IDR
();
CHIP_READY
();
cs4231_chip
->
perchip_info
.
play
.
encoding
=
value
;
return
0
;
}
}
dprintk
((
"output enc failed
\n
"
));
return
-
EINVAL
;
}
static
int
cs4231_get_output_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
encoding
;
}
static
int
cs4231_set_input_encoding
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
,
set_bits
;
tprintk
((
"input encoding %d
\n
"
,
value
));
if
(
value
!=
0
)
{
set_bits
=
cs4231_encoding_to_bits
(
drv
,
value
);
if
(
set_bits
>=
0
)
{
READ_IDR
();
READ_IDR
();
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x1c
);
tmp_bits
=
READ_IDR
();
WRITE_IDR
(
CHANGE_ENCODING
(
tmp_bits
,
set_bits
));
READ_IDR
();
READ_IDR
();
CHIP_READY
();
cs4231_chip
->
perchip_info
.
record
.
encoding
=
value
;
return
0
;
}
}
dprintk
((
"input enc failed
\n
"
));
return
-
EINVAL
;
}
static
int
cs4231_get_input_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
record
.
encoding
;
}
static
int
cs4231_set_output_rate
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
,
set_bits
;
tprintk
((
"output rate %d
\n
"
,
value
));
if
(
value
!=
0
)
{
set_bits
=
cs4231_rate_to_bits
(
drv
,
&
value
);
if
(
set_bits
>=
0
)
{
READ_IDR
();
READ_IDR
();
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x8
);
tmp_bits
=
READ_IDR
();
WRITE_IDR
(
CHANGE_DFR
(
tmp_bits
,
set_bits
));
READ_IDR
();
READ_IDR
();
CHIP_READY
();
cs4231_chip
->
perchip_info
.
play
.
sample_rate
=
value
;
tprintk
((
"tmp_bits[%02x] set_bits[%02x] CHANGE_DFR[%02x]
\n
"
,
tmp_bits
,
set_bits
,
CHANGE_DFR
(
tmp_bits
,
set_bits
)));
return
0
;
}
}
dprintk
((
"output rate failed
\n
"
));
return
-
EINVAL
;
}
static
int
cs4231_get_output_rate
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
sample_rate
;
}
static
int
cs4231_set_input_rate
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
,
set_bits
;
tprintk
((
"input rate %d
\n
"
,
value
));
if
(
value
!=
0
)
{
set_bits
=
cs4231_rate_to_bits
(
drv
,
&
value
);
if
(
set_bits
>=
0
)
{
READ_IDR
();
READ_IDR
();
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x1c
);
tmp_bits
=
READ_IDR
();
WRITE_IDR
(
CHANGE_DFR
(
tmp_bits
,
set_bits
));
READ_IDR
();
READ_IDR
();
CHIP_READY
();
cs4231_chip
->
perchip_info
.
record
.
sample_rate
=
value
;
return
0
;
}
}
dprintk
((
"input rate failed
\n
"
));
return
-
EINVAL
;
}
static
int
cs4231_get_input_rate
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
record
.
sample_rate
;
}
/* Generically we support 4 channels. This hardware does 2 */
static
int
cs4231_set_input_channels
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
;
tprintk
((
"input channels %d
\n
"
,
value
));
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x1c
);
tmp_bits
=
READ_IDR
();
switch
(
value
)
{
case
1
:
WRITE_IDR
(
CS4231_MONO_ON
(
tmp_bits
));
break
;
case
2
:
WRITE_IDR
(
CS4231_STEREO_ON
(
tmp_bits
));
break
;
default:
dprintk
((
"input chan failed
\n
"
));
return
-
EINVAL
;
};
CHIP_READY
();
cs4231_chip
->
perchip_info
.
record
.
channels
=
value
;
return
0
;
}
static
int
cs4231_get_input_channels
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
record
.
channels
;
}
/* Generically we support 4 channels. This hardware does 2 */
static
int
cs4231_set_output_channels
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp_bits
;
tprintk
((
"output channels %d
\n
"
,
value
));
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x8
);
tmp_bits
=
READ_IDR
();
switch
(
value
)
{
case
1
:
WRITE_IDR
(
CS4231_MONO_ON
(
tmp_bits
));
break
;
case
2
:
WRITE_IDR
(
CS4231_STEREO_ON
(
tmp_bits
));
break
;
default:
dprintk
((
"output chan failed
\n
"
));
return
-
EINVAL
;
};
CHIP_READY
();
cs4231_chip
->
perchip_info
.
play
.
channels
=
value
;
return
0
;
}
static
int
cs4231_get_output_channels
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
channels
;
}
static
int
cs4231_get_input_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
record
.
precision
;
}
static
int
cs4231_get_output_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
precision
;
}
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
.
record
.
precision
=
val
;
return
cs4231_chip
->
perchip_info
.
record
.
precision
;
}
static
int
cs4231_set_output_precision
(
struct
sparcaudio_driver
*
drv
,
int
val
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_chip
->
perchip_info
.
play
.
precision
=
val
;
return
cs4231_chip
->
perchip_info
.
play
.
precision
;
}
/* Wait until the auto calibration process has finished */
static
void
cs4231_ready
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
int
x
;
WRITE_IAR
(
IAR_AUTOCAL_END
);
x
=
0
;
do
{
if
(
READ_IDR
()
!=
IAR_NOT_READY
)
break
;
x
++
;
}
while
(
x
<=
CS_TIMEOUT
);
WRITE_IAR
(
0x0b
);
x
=
0
;
do
{
if
(
READ_IDR
()
!=
AUTOCAL_IN_PROGRESS
)
break
;
x
++
;
}
while
(
x
<=
CS_TIMEOUT
);
}
/* Set output mute */
static
int
cs4231_output_muted
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
tprintk
((
"in cs4231_output_muted: %d
\n
"
,
value
));
if
(
!
value
)
{
WRITE_IAR
(
0x7
);
WRITE_IDR
(
READ_IDR
()
&
OUTCR_UNMUTE
);
WRITE_IAR
(
0x6
);
WRITE_IDR
(
READ_IDR
()
&
OUTCR_UNMUTE
);
cs4231_chip
->
perchip_info
.
output_muted
=
0
;
}
else
{
WRITE_IAR
(
0x7
);
WRITE_IDR
(
READ_IDR
()
|
OUTCR_MUTE
);
WRITE_IAR
(
0x6
);
WRITE_IDR
(
READ_IDR
()
|
OUTCR_MUTE
);
cs4231_chip
->
perchip_info
.
output_muted
=
1
;
}
return
0
;
}
static
int
cs4231_get_output_muted
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
output_muted
;
}
static
int
cs4231_get_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
);
}
static
int
cs4231_get_input_ports
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
/* This apparently applies only to APC ultras, not ebus ultras */
if
(
cs4231_chip
->
status
&
CS_STATUS_IS_ULTRA
)
return
(
AUDIO_LINE_IN
|
AUDIO_MICROPHONE
|
AUDIO_ANALOG_LOOPBACK
);
else
return
(
AUDIO_INTERNAL_CD_IN
|
AUDIO_LINE_IN
|
AUDIO_MICROPHONE
|
AUDIO_ANALOG_LOOPBACK
);
}
/* Set chip "output" port */
static
int
cs4231_set_output_port
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
retval
=
0
;
tprintk
((
"output port: %d
\n
"
,
value
));
/* Aaaaaah! It's all coming so fast! Turn it all off, then selectively
* enable things.
*/
WRITE_IAR
(
0x1a
);
WRITE_IDR
(
READ_IDR
()
|
MONO_IOCR_MUTE
);
WRITE_IAR
(
0x0a
);
WRITE_IDR
(
READ_IDR
()
|
PINCR_LINE_MUTE
);
WRITE_IDR
(
READ_IDR
()
|
PINCR_HDPH_MUTE
);
if
(
value
&
AUDIO_SPEAKER
)
{
WRITE_IAR
(
0x1a
);
WRITE_IDR
(
READ_IDR
()
&
~
MONO_IOCR_MUTE
);
retval
|=
AUDIO_SPEAKER
;
}
if
(
value
&
AUDIO_HEADPHONE
)
{
WRITE_IAR
(
0x0a
);
WRITE_IDR
(
READ_IDR
()
&
~
PINCR_HDPH_MUTE
);
retval
|=
AUDIO_HEADPHONE
;
}
if
(
value
&
AUDIO_LINE_OUT
)
{
WRITE_IAR
(
0x0a
);
WRITE_IDR
(
READ_IDR
()
&
~
PINCR_LINE_MUTE
);
retval
|=
AUDIO_LINE_OUT
;
}
cs4231_chip
->
perchip_info
.
play
.
port
=
retval
;
return
(
retval
);
}
static
int
cs4231_get_output_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
port
;
}
/* Set chip "input" port */
static
int
cs4231_set_input_port
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
retval
=
0
;
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. CD input mixable, analog loopback may be.
*/
/* Ultra systems do not support AUDIO_INTERNAL_CD_IN */
/* This apparently applies only to APC ultras, not ebus ultras */
if
(
!
(
cs4231_chip
->
status
&
CS_STATUS_IS_ULTRA
))
{
if
(
value
&
AUDIO_INTERNAL_CD_IN
)
{
WRITE_IAR
(
0x1
);
WRITE_IDR
(
CDROM_ENABLE
(
READ_IDR
()));
WRITE_IAR
(
0x0
);
WRITE_IDR
(
CDROM_ENABLE
(
READ_IDR
()));
retval
=
AUDIO_INTERNAL_CD_IN
;
}
}
if
((
value
&
AUDIO_LINE_IN
))
{
WRITE_IAR
(
0x1
);
WRITE_IDR
(
LINE_ENABLE
(
READ_IDR
()));
WRITE_IAR
(
0x0
);
WRITE_IDR
(
LINE_ENABLE
(
READ_IDR
()));
retval
=
AUDIO_LINE_IN
;
}
else
if
(
value
&
AUDIO_MICROPHONE
)
{
WRITE_IAR
(
0x1
);
WRITE_IDR
(
MIC_ENABLE
(
READ_IDR
()));
WRITE_IAR
(
0x0
);
WRITE_IDR
(
MIC_ENABLE
(
READ_IDR
()));
retval
=
AUDIO_MICROPHONE
;
}
else
if
(
value
&
AUDIO_ANALOG_LOOPBACK
)
{
WRITE_IAR
(
0x1
);
WRITE_IDR
(
OUTPUTLOOP_ENABLE
(
READ_IDR
()));
WRITE_IAR
(
0x0
);
WRITE_IDR
(
OUTPUTLOOP_ENABLE
(
READ_IDR
()));
retval
=
AUDIO_ANALOG_LOOPBACK
;
}
cs4231_chip
->
perchip_info
.
record
.
port
=
retval
;
return
retval
;
}
static
int
cs4231_get_input_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
record
.
port
;
}
/* Set chip "monitor" gain */
static
int
cs4231_set_monitor_volume
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
a
=
0
;
tprintk
((
"monitor gain: %d
\n
"
,
value
));
/* This interpolation really sucks. The question is, be compatible
* with ScumOS/Sloaris or not?
*/
a
=
CS4231_MON_MAX_ATEN
-
(
value
*
(
CS4231_MON_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
));
WRITE_IAR
(
0x0d
);
if
(
a
>=
CS4231_MON_MAX_ATEN
)
WRITE_IDR
(
LOOPB_OFF
);
else
WRITE_IDR
((
a
<<
2
)
|
LOOPB_ON
);
if
(
value
==
AUDIO_MAX_GAIN
)
cs4231_chip
->
perchip_info
.
monitor_gain
=
AUDIO_MAX_GAIN
;
else
cs4231_chip
->
perchip_info
.
monitor_gain
=
((
CS4231_MAX_DEV_ATEN
-
a
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
CS4231_MAX_DEV_ATEN
+
1
));
return
0
;
}
static
int
cs4231_get_monitor_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
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
;
}
#ifdef EB4231_SUPPORT
static
int
eb4231_get_output_samples
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dbcr
=
readl
(
cs4231_chip
->
eb2p
+
EBDMA_COUNT
);
int
count
=
cs4231_length_to_samplecount
(
&
cs4231_chip
->
perchip_info
.
play
,
dbcr
);
return
(
cs4231_chip
->
perchip_info
.
play
.
samples
-
((
count
>
cs4231_chip
->
perchip_info
.
play
.
samples
)
?
0
:
count
));
}
static
int
eb4231_get_input_samples
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dbcr
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_COUNT
);
int
count
=
cs4231_length_to_samplecount
(
&
cs4231_chip
->
perchip_info
.
record
,
dbcr
);
return
(
cs4231_chip
->
perchip_info
.
record
.
samples
-
((
count
>
cs4231_chip
->
perchip_info
.
record
.
samples
)
?
0
:
count
));
}
#endif
static
int
cs4231_get_output_samples
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dmapc
=
sbus_readl
(
cs4231_chip
->
regs
+
APCPC
);
int
count
=
cs4231_length_to_samplecount
(
&
cs4231_chip
->
perchip_info
.
play
,
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
;
u32
dmacc
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCC
);
int
count
=
cs4231_length_to_samplecount
(
&
cs4231_chip
->
perchip_info
.
record
,
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
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_record_gain
(
drv
,
value
,
cs4231_chip
->
perchip_info
.
record
.
balance
);
return
0
;
}
static
int
cs4231_get_input_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
(
int
)
cs4231_chip
->
perchip_info
.
record
.
gain
;
}
static
int
cs4231_set_output_volume
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_play_gain
(
drv
,
value
,
cs4231_chip
->
perchip_info
.
play
.
balance
);
return
0
;
}
static
int
cs4231_get_output_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
cs4231_chip
->
perchip_info
.
play
.
gain
;
}
/* Likewise for balance */
static
int
cs4231_set_input_balance
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_chip
->
perchip_info
.
record
.
balance
=
value
;
cs4231_record_gain
(
drv
,
cs4231_chip
->
perchip_info
.
record
.
gain
,
cs4231_chip
->
perchip_info
.
record
.
balance
);
return
0
;
}
static
int
cs4231_get_input_balance
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
(
int
)
cs4231_chip
->
perchip_info
.
record
.
balance
;
}
static
int
cs4231_set_output_balance
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_chip
->
perchip_info
.
play
.
balance
=
value
;
cs4231_play_gain
(
drv
,
cs4231_chip
->
perchip_info
.
play
.
gain
,
cs4231_chip
->
perchip_info
.
play
.
balance
);
return
0
;
}
static
int
cs4231_get_output_balance
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
return
(
int
)
cs4231_chip
->
perchip_info
.
play
.
balance
;
}
/* Set chip record gain */
static
int
cs4231_record_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp
=
0
,
r
,
l
,
r_adj
,
l_adj
;
unsigned
char
old_gain
;
r
=
l
=
value
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
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_adj
=
l
*
(
CS4231_MAX_GAIN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
);
r_adj
=
r
*
(
CS4231_MAX_GAIN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
);
WRITE_IAR
(
0x0
);
old_gain
=
READ_IDR
();
WRITE_IDR
(
RECGAIN_SET
(
old_gain
,
l_adj
));
WRITE_IAR
(
0x1
);
old_gain
=
READ_IDR
();
WRITE_IDR
(
RECGAIN_SET
(
old_gain
,
r_adj
));
if
(
l
==
value
)
{
(
l
==
0
)
?
(
tmp
=
0
)
:
(
tmp
=
((
l_adj
+
1
)
*
AUDIO_MAX_GAIN
)
/
(
CS4231_MAX_GAIN
+
1
));
}
else
if
(
r
==
value
)
{
(
r
==
0
)
?
(
tmp
=
0
)
:
(
tmp
=
((
r_adj
+
1
)
*
AUDIO_MAX_GAIN
)
/
(
CS4231_MAX_GAIN
+
1
));
}
cs4231_chip
->
perchip_info
.
record
.
gain
=
tmp
;
return
0
;
}
/* Set chip play gain */
static
int
cs4231_play_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
tmp
=
0
,
r
,
l
,
r_adj
,
l_adj
;
unsigned
char
old_gain
;
tprintk
((
"in play_gain: %d %c
\n
"
,
value
,
balance
));
r
=
l
=
value
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
r
=
(
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
==
0
)
?
(
l_adj
=
CS4231_MAX_DEV_ATEN
)
:
(
l_adj
=
CS4231_MAX_ATEN
-
(
l
*
(
CS4231_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
(
r
==
0
)
?
(
r_adj
=
CS4231_MAX_DEV_ATEN
)
:
(
r_adj
=
CS4231_MAX_ATEN
-
(
r
*
(
CS4231_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
WRITE_IAR
(
0x6
);
old_gain
=
READ_IDR
();
WRITE_IDR
(
GAIN_SET
(
old_gain
,
l_adj
));
WRITE_IAR
(
0x7
);
old_gain
=
READ_IDR
();
WRITE_IDR
(
GAIN_SET
(
old_gain
,
r_adj
));
if
((
value
==
0
)
||
(
value
==
AUDIO_MAX_GAIN
))
{
tmp
=
value
;
}
else
{
if
(
value
==
l
)
{
tmp
=
((
CS4231_MAX_ATEN
-
l_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
CS4231_MAX_ATEN
+
1
));
}
else
if
(
value
==
r
)
{
tmp
=
((
CS4231_MAX_ATEN
-
r_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
CS4231_MAX_ATEN
+
1
));
}
}
cs4231_chip
->
perchip_info
.
play
.
gain
=
tmp
;
return
0
;
}
/* Reset the audio chip to a sane state. */
static
void
cs4231_chip_reset
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
unsigned
char
vers
;
tprintk
((
"in cs4231_chip_reset
\n
"
));
if
(
cs4231_chip
->
status
&
CS_STATUS_IS_EBUS
)
{
#ifdef EB4231_SUPPORT
writel
(
EBUS_DCSR_RESET
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
writel
(
EBUS_DCSR_RESET
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
writel
(
EBUS_DCSR_BURST_SZ_16
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
writel
(
EBUS_DCSR_BURST_SZ_16
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
#endif
}
else
{
u32
tmp
;
sbus_writel
(
APC_CHIP_RESET
,
cs4231_chip
->
regs
+
APCCSR
);
sbus_writel
(
0x00
,
cs4231_chip
->
regs
+
APCCSR
);
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
tmp
|=
APC_CDC_RESET
;
sbus_writel
(
tmp
,
cs4231_chip
->
regs
+
APCCSR
);
udelay
(
20
);
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
tmp
&=
~
(
APC_CDC_RESET
);
sbus_writel
(
tmp
,
cs4231_chip
->
regs
+
APCCSR
);
}
WRITE_IAR
(
READ_IAR
()
|
IAR_AUTOCAL_BEGIN
);
CHIP_READY
();
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x0c
);
WRITE_IDR
(
MISC_IR_MODE2
);
/* This is the equivalent of DEFAULT_DATA_FMAT */
cs4231_set_input_encoding
(
drv
,
AUDIO_ENCODING_ULAW
);
cs4231_set_input_rate
(
drv
,
CS4231_RATE
);
cs4231_set_input_channels
(
drv
,
CS4231_CHANNELS
);
cs4231_set_input_precision
(
drv
,
CS4231_PRECISION
);
cs4231_set_output_encoding
(
drv
,
AUDIO_ENCODING_ULAW
);
cs4231_set_output_rate
(
drv
,
CS4231_RATE
);
cs4231_set_output_channels
(
drv
,
CS4231_CHANNELS
);
cs4231_set_output_precision
(
drv
,
CS4231_PRECISION
);
WRITE_IAR
(
0x19
);
/* see what we can turn on */
vers
=
READ_IDR
();
if
(
vers
&
CS4231A
)
{
tprintk
((
"This is a CS4231A
\n
"
));
cs4231_chip
->
status
|=
CS_STATUS_REV_A
;
}
else
{
cs4231_chip
->
status
&=
~
CS_STATUS_REV_A
;
}
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x10
);
WRITE_IDR
(
OLB_ENABLE
);
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x11
);
if
(
cs4231_chip
->
status
&
CS_STATUS_REV_A
)
WRITE_IDR
(
HPF_ON
|
XTALE_ON
);
else
WRITE_IDR
(
HPF_ON
);
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x1a
);
WRITE_IDR
(
0x00
);
/* Now set things up for defaults */
cs4231_set_input_balance
(
drv
,
AUDIO_MID_BALANCE
);
cs4231_set_output_balance
(
drv
,
AUDIO_MID_BALANCE
);
cs4231_set_input_volume
(
drv
,
CS4231_DEFAULT_RECGAIN
);
cs4231_set_output_volume
(
drv
,
CS4231_DEFAULT_PLAYGAIN
);
cs4231_set_input_port
(
drv
,
AUDIO_MICROPHONE
);
cs4231_set_output_port
(
drv
,
AUDIO_SPEAKER
);
cs4231_set_monitor_volume
(
drv
,
LOOPB_OFF
);
WRITE_IAR
(
IAR_AUTOCAL_END
);
cs4231_ready
(
drv
);
WRITE_IAR
(
IAR_AUTOCAL_BEGIN
|
0x09
);
WRITE_IDR
(
READ_IDR
()
&
ACAL_DISABLE
);
WRITE_IAR
(
IAR_AUTOCAL_END
);
cs4231_ready
(
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
cs4231_length_to_samplecount
(
struct
audio_prinfo
*
thisdir
,
unsigned
int
length
)
{
unsigned
int
count
;
if
(
thisdir
->
channels
==
2
)
count
=
(
length
/
2
);
else
count
=
length
;
if
(
thisdir
->
encoding
==
AUDIO_ENCODING_LINEAR
)
count
=
(
count
/
2
);
else
if
(
thisdir
->
encoding
==
AUDIO_ENCODING_DVI
)
count
=
(
count
/
4
);
return
count
;
}
#ifdef EB4231_SUPPORT
static
void
eb4231_getsamplecount
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
length
,
unsigned
int
direction
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
struct
audio_prinfo
*
thisdir
;
unsigned
int
count
,
curcount
,
nextcount
,
dbcr
;
if
(
direction
==
1
)
{
thisdir
=
&
cs4231_chip
->
perchip_info
.
record
;
dbcr
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_COUNT
);
nextcount
=
cs4231_chip
->
input_next_dma_size
;
}
else
{
thisdir
=
&
cs4231_chip
->
perchip_info
.
play
;
dbcr
=
readl
(
cs4231_chip
->
eb2p
+
EBDMA_COUNT
);
nextcount
=
cs4231_chip
->
output_next_dma_size
;
}
curcount
=
cs4231_length_to_samplecount
(
thisdir
,
dbcr
);
count
=
thisdir
->
samples
;
length
=
cs4231_length_to_samplecount
(
thisdir
,
length
);
/* normalize for where we are. */
thisdir
->
samples
=
((
count
-
nextcount
)
+
(
length
-
curcount
));
}
#endif
static
void
cs4231_getsamplecount
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
length
,
unsigned
int
direction
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
struct
audio_prinfo
*
thisdir
;
unsigned
int
count
,
nextcount
,
curcount
;
u32
tmp
;
if
(
direction
==
1
)
{
/* record */
thisdir
=
&
cs4231_chip
->
perchip_info
.
record
;
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCC
);
curcount
=
cs4231_length_to_samplecount
(
thisdir
,
tmp
);
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCNC
);
nextcount
=
cs4231_length_to_samplecount
(
thisdir
,
tmp
);
}
else
{
/* play */
thisdir
=
&
cs4231_chip
->
perchip_info
.
play
;
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCPC
);
curcount
=
cs4231_length_to_samplecount
(
thisdir
,
tmp
);
tmp
=
sbus_readl
(
cs4231_chip
->
regs
+
APCPNC
);
nextcount
=
cs4231_length_to_samplecount
(
thisdir
,
tmp
);
}
count
=
thisdir
->
samples
;
length
=
cs4231_length_to_samplecount
(
thisdir
,
length
);
/* normalize for where we are. */
thisdir
->
samples
=
((
count
-
nextcount
)
+
(
length
-
curcount
));
}
static
int
cs4231_open
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
/* Set the default audio parameters if not already in use. */
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
if
(
!
(
drv
->
flags
&
SDF_OPEN_WRITE
)
&&
(
cs4231_chip
->
perchip_info
.
play
.
active
==
0
))
{
cs4231_chip
->
perchip_info
.
play
.
open
=
1
;
cs4231_chip
->
perchip_info
.
play
.
samples
=
cs4231_chip
->
perchip_info
.
play
.
error
=
0
;
}
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
if
(
!
(
drv
->
flags
&
SDF_OPEN_READ
)
&&
(
cs4231_chip
->
perchip_info
.
record
.
active
==
0
))
{
cs4231_chip
->
perchip_info
.
record
.
open
=
1
;
cs4231_chip
->
perchip_info
.
record
.
samples
=
cs4231_chip
->
perchip_info
.
record
.
error
=
0
;
}
}
cs4231_ready
(
drv
);
CHIP_READY
();
MOD_INC_USE_COUNT
;
return
0
;
}
static
void
cs4231_release
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
void
(
*
dma_unmap_single
)(
struct
sbus_dev
*
,
dma_addr_t
,
size_t
,
int
)
=
sbus_unmap_single
;
#ifdef EB4231_SUPPORT
if
(
cs4231_chip
->
status
&
CS_STATUS_IS_EBUS
)
dma_unmap_single
=
(
void
(
*
)(
struct
sbus_dev
*
,
dma_addr_t
,
size_t
,
int
))
pci_unmap_single
;
#endif
/* zero out any info about what data we have as well */
if
(
file
->
f_mode
&
FMODE_READ
)
{
/* stop capture here or midlevel? */
cs4231_chip
->
perchip_info
.
record
.
open
=
0
;
if
(
cs4231_chip
->
input_dma_handle
)
{
dma_unmap_single
(
drv
->
dev
,
cs4231_chip
->
input_dma_handle
,
cs4231_chip
->
input_dma_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_dma_handle
=
0
;
cs4231_chip
->
input_dma_size
=
0
;
}
if
(
cs4231_chip
->
input_next_dma_handle
)
{
dma_unmap_single
(
drv
->
dev
,
cs4231_chip
->
input_next_dma_handle
,
cs4231_chip
->
input_next_dma_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_next_dma_handle
=
0
;
cs4231_chip
->
input_next_dma_size
=
0
;
}
}
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
)
{
dma_unmap_single
(
drv
->
dev
,
cs4231_chip
->
output_dma_handle
,
cs4231_chip
->
output_dma_size
,
SBUS_DMA_TODEVICE
);
cs4231_chip
->
output_dma_handle
=
0
;
cs4231_chip
->
output_dma_size
=
0
;
}
if
(
cs4231_chip
->
output_next_dma_handle
)
{
dma_unmap_single
(
drv
->
dev
,
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
output_next_dma_size
,
SBUS_DMA_TODEVICE
);
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
&&
(
cs4231_chip
->
status
&
CS_STATUS_INIT_ON_CLOSE
))
{
cs4231_chip_reset
(
drv
);
cs4231_chip
->
status
&=
~
CS_STATUS_INIT_ON_CLOSE
;
}
MOD_DEC_USE_COUNT
;
}
static
void
cs4231_playintr
(
struct
sparcaudio_driver
*
drv
,
int
push
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
status
=
0
;
if
(
!
push
)
{
if
(
!
cs4231_chip
->
perchip_info
.
play
.
active
)
{
sbus_writel
(
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
regs
+
APCPNVA
);
sbus_writel
(
cs4231_chip
->
output_next_dma_size
,
cs4231_chip
->
regs
+
APCPNC
);
}
sparcaudio_output_done
(
drv
,
0
);
return
;
}
if
(
cs4231_chip
->
playlen
==
0
&&
cs4231_chip
->
output_size
>
0
)
cs4231_chip
->
playlen
=
cs4231_chip
->
output_size
;
if
(
cs4231_chip
->
output_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
output_dma_handle
,
cs4231_chip
->
output_dma_size
,
SBUS_DMA_TODEVICE
);
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_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
->
perchip_info
.
play
.
pause
))
{
cs4231_chip
->
output_next_dma_handle
=
sbus_map_single
(
drv
->
dev
,
(
char
*
)
cs4231_chip
->
output_ptr
,
cs4231_chip
->
output_size
,
SBUS_DMA_TODEVICE
);
cs4231_chip
->
output_next_dma_size
=
cs4231_chip
->
output_size
;
sbus_writel
(
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
regs
+
APCPNVA
);
sbus_writel
(
cs4231_chip
->
output_next_dma_size
,
cs4231_chip
->
regs
+
APCPNC
);
cs4231_chip
->
output_size
=
0
;
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
playing_count
++
;
status
+=
2
;
}
else
{
sbus_writel
(
0
,
cs4231_chip
->
regs
+
APCPNVA
);
sbus_writel
(
0
,
cs4231_chip
->
regs
+
APCPNC
);
}
sparcaudio_output_done
(
drv
,
status
);
}
#ifdef EB4231_SUPPORT
static
void
eb4231_playintr
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
status
=
0
;
if
(
cs4231_chip
->
playlen
==
0
&&
cs4231_chip
->
output_size
>
0
)
cs4231_chip
->
playlen
=
cs4231_chip
->
output_size
;
if
(
cs4231_chip
->
output_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
output_dma_handle
,
cs4231_chip
->
output_dma_size
,
PCI_DMA_TODEVICE
);
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_dma_size
=
cs4231_chip
->
output_next_dma_size
;
cs4231_chip
->
output_next_dma_handle
=
0
;
cs4231_chip
->
output_next_dma_size
=
0
;
}
if
((
cs4231_chip
->
output_ptr
&&
cs4231_chip
->
output_size
>
0
)
&&
!
(
cs4231_chip
->
perchip_info
.
play
.
pause
))
{
cs4231_chip
->
output_next_dma_handle
=
pci_map_single
((
struct
pci_dev
*
)
drv
->
dev
,
(
char
*
)
cs4231_chip
->
output_ptr
,
cs4231_chip
->
output_size
,
PCI_DMA_TODEVICE
);
cs4231_chip
->
output_next_dma_size
=
cs4231_chip
->
output_size
;
writel
(
cs4231_chip
->
output_next_dma_size
,
cs4231_chip
->
eb2p
+
EBDMA_COUNT
);
writel
(
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
eb2p
+
EBDMA_ADDR
);
cs4231_chip
->
output_size
=
0
;
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
playing_count
++
;
status
+=
2
;
}
sparcaudio_output_done
(
drv
,
status
);
}
#endif
static
void
cs4231_recclear
(
int
fmt
,
char
*
dmabuf
,
int
length
)
{
switch
(
fmt
)
{
case
AUDIO_ENCODING_LINEAR
:
memset
(
dmabuf
,
0x00
,
length
);
break
;
case
AUDIO_ENCODING_ALAW
:
memset
(
dmabuf
,
0xd5
,
length
);
break
;
case
AUDIO_ENCODING_ULAW
:
memset
(
dmabuf
,
0xff
,
length
);
break
;
}
}
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_disable_rec
(
drv
);
}
if
(
cs4231_chip
->
input_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
input_dma_handle
,
cs4231_chip
->
input_dma_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_dma_handle
=
0
;
cs4231_chip
->
input_dma_size
=
0
;
cs4231_chip
->
recording_count
--
;
status
++
;
}
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
=
sbus_map_single
(
drv
->
dev
,
(
char
*
)
cs4231_chip
->
input_ptr
,
cs4231_chip
->
input_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_next_dma_size
=
cs4231_chip
->
input_size
;
sbus_writel
(
cs4231_chip
->
input_next_dma_handle
,
cs4231_chip
->
regs
+
APCCNVA
);
sbus_writel
(
cs4231_chip
->
input_next_dma_size
,
cs4231_chip
->
regs
+
APCCNC
);
cs4231_chip
->
input_size
=
0
;
cs4231_chip
->
input_ptr
=
NULL
;
cs4231_chip
->
recording_count
++
;
status
+=
2
;
}
else
{
sbus_writel
(
0
,
cs4231_chip
->
regs
+
APCCNVA
);
sbus_writel
(
0
,
cs4231_chip
->
regs
+
APCCNC
);
}
sparcaudio_input_done
(
drv
,
status
);
return
1
;
}
#ifdef EB4231_SUPPORT
static
int
eb4231_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
"
));
eb4231_pollinput
(
drv
);
cs4231_disable_rec
(
drv
);
}
if
(
cs4231_chip
->
input_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
input_dma_handle
,
cs4231_chip
->
input_dma_size
,
PCI_DMA_FROMDEVICE
);
cs4231_chip
->
input_dma_handle
=
0
;
cs4231_chip
->
input_dma_size
=
0
;
cs4231_chip
->
recording_count
--
;
status
++
;
}
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
=
pci_map_single
((
struct
pci_dev
*
)
drv
->
dev
,
(
char
*
)
cs4231_chip
->
input_ptr
,
cs4231_chip
->
input_size
,
PCI_DMA_FROMDEVICE
);
cs4231_chip
->
input_next_dma_size
=
cs4231_chip
->
input_size
;
writel
(
cs4231_chip
->
input_next_dma_size
,
cs4231_chip
->
eb2c
+
EBDMA_COUNT
);
writel
(
cs4231_chip
->
input_next_dma_handle
,
cs4231_chip
->
eb2c
+
EBDMA_ADDR
);
cs4231_chip
->
input_size
=
0
;
cs4231_chip
->
input_ptr
=
NULL
;
cs4231_chip
->
recording_count
++
;
status
+=
2
;
}
sparcaudio_input_done
(
drv
,
status
);
return
1
;
}
#endif
#ifdef EB4231_SUPPORT
static
void
eb4231_start_output
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dcsr
;
cs4231_chip
->
output_ptr
=
buffer
;
cs4231_chip
->
output_size
=
count
;
if
(
cs4231_chip
->
perchip_info
.
play
.
active
||
(
cs4231_chip
->
perchip_info
.
play
.
pause
))
return
;
cs4231_ready
(
drv
);
cs4231_chip
->
perchip_info
.
play
.
active
=
1
;
cs4231_chip
->
playing_count
=
0
;
dcsr
=
readl
(
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
if
(
!
(
dcsr
&
EBUS_DCSR_EN_DMA
))
{
writel
(
EBUS_DCSR_RESET
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
writel
(
EBUS_DCSR_BURST_SZ_16
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
eb4231_playintr
(
drv
);
writel
(
EBUS_DCSR_BURST_SZ_16
|
(
EBUS_DCSR_EN_DMA
|
EBUS_DCSR_INT_EN
|
EBUS_DCSR_EN_CNT
|
EBUS_DCSR_EN_NEXT
),
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
cs4231_enable_play
(
drv
);
cs4231_ready
(
drv
);
}
else
{
eb4231_playintr
(
drv
);
}
}
#endif
static
void
cs4231_start_output
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
csr
;
tprintk
((
"in 4231 start output
\n
"
));
cs4231_chip
->
output_ptr
=
buffer
;
cs4231_chip
->
output_size
=
count
;
if
(
cs4231_chip
->
perchip_info
.
play
.
active
||
(
cs4231_chip
->
perchip_info
.
play
.
pause
))
return
;
cs4231_ready
(
drv
);
cs4231_chip
->
perchip_info
.
play
.
active
=
1
;
cs4231_chip
->
playing_count
=
0
;
csr
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
if
((
csr
&
APC_PPAUSE
)
||
!
(
csr
&
APC_PDMA_READY
))
{
u32
pnva
;
csr
&=
~
APC_XINT_PLAY
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
csr
&=
~
APC_PPAUSE
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
pnva
=
sbus_readl
(
cs4231_chip
->
regs
+
APCPNVA
);
cs4231_playintr
(
drv
,
(
pnva
==
0
)
?
1
:
0
);
csr
|=
APC_PLAY_SETUP
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
cs4231_enable_play
(
drv
);
cs4231_ready
(
drv
);
}
}
#ifdef EB4231_SUPPORT
static
void
eb4231_stop_output
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dcsr
;
dprintk
((
"eb4231_stop_output: dcsr 0x%x dacr 0x%x dbcr %d
\n
"
,
readl
(
cs4231_chip
->
eb2p
+
EBDMA_CSR
),
readl
(
cs4231_chip
->
eb2p
+
EBDMA_ADDR
),
readl
(
cs4231_chip
->
eb2p
+
EBDMA_COUNT
)));
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
output_size
=
0
;
if
(
cs4231_chip
->
output_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
output_dma_handle
,
cs4231_chip
->
output_dma_size
,
PCI_DMA_TODEVICE
);
cs4231_chip
->
output_dma_handle
=
0
;
cs4231_chip
->
output_dma_size
=
0
;
}
if
(
cs4231_chip
->
output_next_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
output_next_dma_size
,
PCI_DMA_TODEVICE
);
cs4231_chip
->
output_next_dma_handle
=
0
;
cs4231_chip
->
output_next_dma_size
=
0
;
}
dcsr
=
readl
(
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
if
(
dcsr
&
EBUS_DCSR_EN_DMA
)
writel
(
dcsr
&
~
EBUS_DCSR_EN_DMA
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
/* Else subsequent speed setting changes are ignored by the chip. */
cs4231_disable_play
(
drv
);
}
#endif
static
void
cs4231_stop_output
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
tprintk
((
"in cs4231_stop_output
\n
"
));
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
output_size
=
0
;
if
(
cs4231_chip
->
output_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
output_dma_handle
,
cs4231_chip
->
output_dma_size
,
SBUS_DMA_TODEVICE
);
cs4231_chip
->
output_dma_handle
=
0
;
cs4231_chip
->
output_dma_size
=
0
;
}
if
(
cs4231_chip
->
output_next_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
output_next_dma_handle
,
cs4231_chip
->
output_next_dma_size
,
SBUS_DMA_TODEVICE
);
cs4231_chip
->
output_next_dma_handle
=
0
;
cs4231_chip
->
output_next_dma_size
=
0
;
}
#if 0 /* Not safe without shutting off the DMA controller as well. -DaveM */
/* Else subsequent speed setting changes are ignored by the chip. */
cs4231_disable_play(drv);
#endif
}
#ifdef EB4231_SUPPORT
static
void
eb4231_pollinput
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
x
;
u32
dcsr
;
x
=
0
;
do
{
dcsr
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
if
(
dcsr
&
EBUS_DCSR_TC
)
break
;
x
++
;
}
while
(
x
<=
CS_TIMEOUT
);
writel
(
dcsr
|
EBUS_DCSR_TC
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
}
#endif
static
void
cs4231_pollinput
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
x
;
u32
csr
;
x
=
0
;
do
{
csr
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
if
(
csr
&
APC_XINT_COVF
)
break
;
x
++
;
}
while
(
x
<=
CS_TIMEOUT
);
sbus_writel
(
csr
|
APC_XINT_CEMP
,
cs4231_chip
->
regs
+
APCCSR
);
}
static
void
cs4231_start_input
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
csr
;
cs4231_chip
->
input_ptr
=
buffer
;
cs4231_chip
->
input_size
=
count
;
if
(
cs4231_chip
->
perchip_info
.
record
.
active
||
(
cs4231_chip
->
perchip_info
.
record
.
pause
))
return
;
cs4231_ready
(
drv
);
cs4231_chip
->
perchip_info
.
record
.
active
=
1
;
cs4231_chip
->
recording_count
=
0
;
csr
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
if
((
csr
&
APC_CPAUSE
)
||
!
(
csr
&
APC_CDMA_READY
))
{
csr
&=
~
APC_XINT_CAPT
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
csr
&=
~
APC_CPAUSE
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
cs4231_recintr
(
drv
);
csr
|=
APC_CAPT_SETUP
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
cs4231_enable_rec
(
drv
);
cs4231_ready
(
drv
);
}
else
{
cs4231_recintr
(
drv
);
}
}
static
void
cs4231_stop_input
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
csr
;
cs4231_chip
->
perchip_info
.
record
.
active
=
0
;
csr
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
csr
|=
APC_CPAUSE
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
cs4231_chip
->
input_ptr
=
NULL
;
cs4231_chip
->
input_size
=
0
;
if
(
cs4231_chip
->
input_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
input_dma_handle
,
cs4231_chip
->
input_dma_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_dma_handle
=
0
;
cs4231_chip
->
input_dma_size
=
0
;
}
if
(
cs4231_chip
->
input_next_dma_handle
)
{
sbus_unmap_single
(
drv
->
dev
,
cs4231_chip
->
input_next_dma_handle
,
cs4231_chip
->
input_next_dma_size
,
SBUS_DMA_FROMDEVICE
);
cs4231_chip
->
input_next_dma_handle
=
0
;
cs4231_chip
->
input_next_dma_size
=
0
;
}
cs4231_pollinput
(
drv
);
}
#ifdef EB4231_SUPPORT
static
void
eb4231_start_input
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dcsr
;
cs4231_chip
->
input_ptr
=
buffer
;
cs4231_chip
->
input_size
=
count
;
if
(
cs4231_chip
->
perchip_info
.
record
.
active
||
(
cs4231_chip
->
perchip_info
.
record
.
pause
))
return
;
cs4231_ready
(
drv
);
cs4231_chip
->
perchip_info
.
record
.
active
=
1
;
cs4231_chip
->
recording_count
=
0
;
dcsr
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
if
(
!
(
dcsr
&
EBUS_DCSR_EN_DMA
))
{
writel
(
EBUS_DCSR_RESET
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
writel
(
EBUS_DCSR_BURST_SZ_16
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
eb4231_recintr
(
drv
);
writel
(
EBUS_DCSR_BURST_SZ_16
|
(
EBUS_DCSR_EN_DMA
|
EBUS_DCSR_INT_EN
|
EBUS_DCSR_EN_CNT
|
EBUS_DCSR_EN_NEXT
),
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
cs4231_enable_rec
(
drv
);
cs4231_ready
(
drv
);
}
else
{
eb4231_recintr
(
drv
);
}
}
static
void
eb4231_stop_input
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dcsr
;
cs4231_chip
->
perchip_info
.
record
.
active
=
0
;
cs4231_chip
->
input_ptr
=
NULL
;
cs4231_chip
->
input_size
=
0
;
if
(
cs4231_chip
->
input_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
input_dma_handle
,
cs4231_chip
->
input_dma_size
,
PCI_DMA_FROMDEVICE
);
cs4231_chip
->
input_dma_handle
=
0
;
cs4231_chip
->
input_dma_size
=
0
;
}
if
(
cs4231_chip
->
input_next_dma_handle
)
{
pci_unmap_single
((
struct
pci_dev
*
)
drv
->
dev
,
cs4231_chip
->
input_next_dma_handle
,
cs4231_chip
->
input_next_dma_size
,
PCI_DMA_FROMDEVICE
);
cs4231_chip
->
input_next_dma_handle
=
0
;
cs4231_chip
->
input_next_dma_size
=
0
;
}
dcsr
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
if
(
dcsr
&
EBUS_DCSR_EN_DMA
)
writel
(
dcsr
&
~
EBUS_DCSR_EN_DMA
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
cs4231_disable_rec
(
drv
);
}
#endif
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
);
return
value
;
}
static
int
cs4231_set_output_error
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
i
;
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
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
i
;
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
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
i
;
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
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
i
;
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
,
audio_device_t
*
audinfo
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
strncpy
(
audinfo
->
name
,
"SUNW,CS4231"
,
sizeof
(
audinfo
->
name
)
-
1
);
/* versions */
/* a: SPARCstation 4/5 b: Ultra 1/2 (electron) */
/* c: Ultra 1/2 PCI? (positron) d: ppc */
/* e: x86 f: Ultra Enterprise? (tazmo) */
/* g: Ultra 30? (quark) h: Ultra 5/10? (darwin) */
/* apparently Ultra 1, Ultra 2 don't have internal CD input */
if
(
cs4231_chip
->
status
&
CS_STATUS_IS_ULTRA
)
strncpy
(
audinfo
->
version
,
"b"
,
sizeof
(
audinfo
->
version
)
-
1
);
else
strncpy
(
audinfo
->
version
,
"a"
,
sizeof
(
audinfo
->
version
)
-
1
);
strncpy
(
audinfo
->
config
,
"onboard1"
,
sizeof
(
audinfo
->
config
)
-
1
);
}
static
int
cs4231_audio_getdev_sunos
(
struct
sparcaudio_driver
*
drv
)
{
return
AUDIO_DEV_CS4231
;
}
static
void
cs4231_loopback
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
value
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
WRITE_IAR
(
0x0d
);
WRITE_IDR
(
value
?
LOOPB_ON
:
0
);
}
static
int
cs4231_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
,
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
int
retval
=
0
;
switch
(
cmd
)
{
case
AUDIO_DIAG_LOOPBACK
:
cs4231_chip
->
status
|=
CS_STATUS_INIT_ON_CLOSE
;
cs4231_loopback
(
drv
,
(
unsigned
int
)
arg
);
break
;
default:
retval
=
-
EINVAL
;
};
return
retval
;
}
#ifdef EB4231_SUPPORT
/* ebus audio capture interrupt handler. */
void
eb4231_cinterrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
dev_id
;
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dummy
;
/* Clear the interrupt. */
dummy
=
readl
(
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
writel
(
dummy
,
cs4231_chip
->
eb2c
+
EBDMA_CSR
);
if
((
dummy
&
EBUS_DCSR_TC
)
!=
0
/*&& (dummy & EBUS_DCSR_A_LOADED) != 0*/
)
{
cs4231_chip
->
perchip_info
.
record
.
samples
+=
cs4231_length_to_samplecount
(
&
(
cs4231_chip
->
perchip_info
.
record
),
cs4231_chip
->
reclen
);
eb4231_recintr
(
drv
);
}
if
((
dummy
&
EBUS_DCSR_A_LOADED
)
==
0
)
{
cs4231_chip
->
perchip_info
.
record
.
active
=
0
;
eb4231_recintr
(
drv
);
eb4231_getsamplecount
(
drv
,
cs4231_chip
->
reclen
,
1
);
}
}
/* ebus audio play interrupt handler. */
void
eb4231_pinterrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
dev_id
;
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
u32
dummy
;
/* Clear the interrupt. Bleh, when not using the next-address
* feature, TC can only be cleared by a reset.
*/
dummy
=
readl
(
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
writel
(
dummy
,
cs4231_chip
->
eb2p
+
EBDMA_CSR
);
/* If we get a terminal count and address loaded condition,
* this means the DNAR was copied into DACR.
*/
if
((
dummy
&
EBUS_DCSR_TC
)
!=
0
/*&& (dummy & EBUS_DCSR_A_LOADED) != 0*/
)
{
cs4231_chip
->
perchip_info
.
play
.
samples
+=
cs4231_length_to_samplecount
(
&
(
cs4231_chip
->
perchip_info
.
play
),
cs4231_chip
->
playlen
);
eb4231_playintr
(
drv
);
}
if
((
dummy
&
EBUS_DCSR_A_LOADED
)
==
0
)
{
cs4231_chip
->
perchip_info
.
play
.
active
=
0
;
eb4231_playintr
(
drv
);
eb4231_getsamplecount
(
drv
,
cs4231_chip
->
playlen
,
0
);
}
}
#endif
/* Audio interrupt handler. */
void
cs4231_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
dev_id
;
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
__u32
dummy
;
dprintk
((
"in cs4231_interrupt
\n
"
));
/* Clear the interrupt. */
dummy
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
sbus_writel
(
dummy
,
cs4231_chip
->
regs
+
APCCSR
);
/* now go through and figure out what gets to claim the interrupt
* if anything since we may be doing shared interrupts
*/
if
(
dummy
&
APC_PLAY_INT
)
{
if
(
dummy
&
APC_XINT_PNVA
)
{
cs4231_chip
->
perchip_info
.
play
.
samples
+=
cs4231_length_to_samplecount
(
&
(
cs4231_chip
->
perchip_info
.
play
),
cs4231_chip
->
playlen
);
if
(
!
(
dummy
&
APC_XINT_EMPT
))
cs4231_playintr
(
drv
,
1
);
}
/* Any other conditions we need worry about? */
}
if
(
dummy
&
APC_CAPT_INT
)
{
if
(
dummy
&
APC_XINT_CNVA
)
{
cs4231_chip
->
perchip_info
.
record
.
samples
+=
cs4231_length_to_samplecount
(
&
(
cs4231_chip
->
perchip_info
.
record
),
cs4231_chip
->
reclen
);
cs4231_recintr
(
drv
);
}
/* Any other conditions we need worry about? */
}
if
(
dummy
&
APC_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
&
APC_XINT_EMPT
)
{
if
(
!
cs4231_chip
->
output_next_dma_handle
)
{
u32
csr
=
sbus_readl
(
cs4231_chip
->
regs
+
APCCSR
);
csr
|=
APC_PPAUSE
;
sbus_writel
(
csr
,
cs4231_chip
->
regs
+
APCCSR
);
cs4231_disable_play
(
drv
);
cs4231_chip
->
perchip_info
.
play
.
error
=
1
;
}
cs4231_chip
->
perchip_info
.
play
.
active
=
0
;
cs4231_playintr
(
drv
,
0
);
cs4231_getsamplecount
(
drv
,
cs4231_chip
->
playlen
,
0
);
}
if
(
dummy
&
APC_GENL_INT
)
{
/* If we get here we must be sharing an interrupt, but I haven't code
* to handle this right now.
*/
}
}
static
struct
sparcaudio_operations
cs4231_ops
=
{
cs4231_open
,
cs4231_release
,
cs4231_ioctl
,
cs4231_start_output
,
cs4231_stop_output
,
cs4231_start_input
,
cs4231_stop_input
,
cs4231_audio_getdev
,
cs4231_set_output_volume
,
cs4231_get_output_volume
,
cs4231_set_input_volume
,
cs4231_get_input_volume
,
cs4231_set_monitor_volume
,
cs4231_get_monitor_volume
,
cs4231_set_output_balance
,
cs4231_get_output_balance
,
cs4231_set_input_balance
,
cs4231_get_input_balance
,
cs4231_set_output_channels
,
cs4231_get_output_channels
,
cs4231_set_input_channels
,
cs4231_get_input_channels
,
cs4231_set_output_precision
,
cs4231_get_output_precision
,
cs4231_set_input_precision
,
cs4231_get_input_precision
,
cs4231_set_output_port
,
cs4231_get_output_port
,
cs4231_set_input_port
,
cs4231_get_input_port
,
cs4231_set_output_encoding
,
cs4231_get_output_encoding
,
cs4231_set_input_encoding
,
cs4231_get_input_encoding
,
cs4231_set_output_rate
,
cs4231_get_output_rate
,
cs4231_set_input_rate
,
cs4231_get_input_rate
,
cs4231_audio_getdev_sunos
,
cs4231_get_output_ports
,
cs4231_get_input_ports
,
cs4231_output_muted
,
cs4231_get_output_muted
,
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
,
};
#ifdef EB4231_SUPPORT
static
struct
sparcaudio_operations
eb4231_ops
=
{
cs4231_open
,
cs4231_release
,
cs4231_ioctl
,
eb4231_start_output
,
eb4231_stop_output
,
eb4231_start_input
,
eb4231_stop_input
,
cs4231_audio_getdev
,
cs4231_set_output_volume
,
cs4231_get_output_volume
,
cs4231_set_input_volume
,
cs4231_get_input_volume
,
cs4231_set_monitor_volume
,
cs4231_get_monitor_volume
,
cs4231_set_output_balance
,
cs4231_get_output_balance
,
cs4231_set_input_balance
,
cs4231_get_input_balance
,
cs4231_set_output_channels
,
cs4231_get_output_channels
,
cs4231_set_input_channels
,
cs4231_get_input_channels
,
cs4231_set_output_precision
,
cs4231_get_output_precision
,
cs4231_set_input_precision
,
cs4231_get_input_precision
,
cs4231_set_output_port
,
cs4231_get_output_port
,
cs4231_set_input_port
,
cs4231_get_input_port
,
cs4231_set_output_encoding
,
cs4231_get_output_encoding
,
cs4231_set_input_encoding
,
cs4231_get_input_encoding
,
cs4231_set_output_rate
,
cs4231_get_output_rate
,
cs4231_set_input_rate
,
cs4231_get_input_rate
,
cs4231_audio_getdev_sunos
,
cs4231_get_output_ports
,
cs4231_get_input_ports
,
cs4231_output_muted
,
cs4231_get_output_muted
,
cs4231_set_output_pause
,
cs4231_get_output_pause
,
cs4231_set_input_pause
,
cs4231_get_input_pause
,
cs4231_set_output_samples
,
eb4231_get_output_samples
,
cs4231_set_input_samples
,
eb4231_get_input_samples
,
cs4231_set_output_error
,
cs4231_get_output_error
,
cs4231_set_input_error
,
cs4231_get_input_error
,
cs4231_get_formats
,
};
#endif
/* Attach to an cs4231 chip given its PROM node. */
static
int
cs4231_attach
(
struct
sparcaudio_driver
*
drv
,
struct
sbus_dev
*
sdev
)
{
struct
cs4231_chip
*
cs4231_chip
;
int
err
;
/* Allocate our private information structure. */
drv
->
private
=
kmalloc
(
sizeof
(
struct
cs4231_chip
),
GFP_KERNEL
);
if
(
drv
->
private
==
NULL
)
return
-
ENOMEM
;
/* Point at the information structure and initialize it. */
drv
->
ops
=
&
cs4231_ops
;
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_chip
->
input_ptr
=
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
input_size
=
cs4231_chip
->
output_size
=
0
;
cs4231_chip
->
status
=
0
;
drv
->
dev
=
sdev
;
/* Map the registers into memory. */
cs4231_chip
->
regs_size
=
sdev
->
reg_addrs
[
0
].
reg_size
;
cs4231_chip
->
regs
=
sbus_ioremap
(
&
sdev
->
resource
[
0
],
0
,
sdev
->
reg_addrs
[
0
].
reg_size
,
"cs4231"
);
if
(
!
cs4231_chip
->
regs
)
{
printk
(
KERN_ERR
"cs4231: could not remap registers
\n
"
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
/* Attach the interrupt handler to the audio interrupt. */
cs4231_chip
->
irq
=
sdev
->
irqs
[
0
];
request_irq
(
cs4231_chip
->
irq
,
cs4231_interrupt
,
SA_SHIRQ
,
"cs4231"
,
drv
);
cs4231_chip
->
nirqs
=
1
;
cs4231_enable_interrupts
(
drv
);
/* Reset the audio chip. */
cs4231_chip_reset
(
drv
);
/* Register ourselves with the midlevel audio driver. */
err
=
register_sparcaudio_driver
(
drv
,
1
);
if
(
err
<
0
)
{
printk
(
KERN_ERR
"cs4231: unable to register
\n
"
);
cs4231_disable_interrupts
(
drv
);
free_irq
(
cs4231_chip
->
irq
,
drv
);
sbus_iounmap
(
cs4231_chip
->
regs
,
cs4231_chip
->
regs_size
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
cs4231_chip
->
perchip_info
.
play
.
active
=
cs4231_chip
->
perchip_info
.
play
.
pause
=
0
;
cs4231_chip
->
perchip_info
.
record
.
active
=
cs4231_chip
->
perchip_info
.
record
.
pause
=
0
;
cs4231_chip
->
perchip_info
.
play
.
avail_ports
=
(
AUDIO_HEADPHONE
|
AUDIO_SPEAKER
|
AUDIO_LINE_OUT
);
cs4231_chip
->
perchip_info
.
record
.
avail_ports
=
(
AUDIO_INTERNAL_CD_IN
|
AUDIO_LINE_IN
|
AUDIO_MICROPHONE
|
AUDIO_ANALOG_LOOPBACK
);
/* Announce the hardware to the user. */
printk
(
KERN_INFO
"audio%d: cs4231%c at %lx irq %s
\n
"
,
drv
->
index
,
(
cs4231_chip
->
status
&
CS_STATUS_REV_A
)
?
'a'
:
' '
,
cs4231_chip
->
regs
,
__irq_itoa
(
cs4231_chip
->
irq
));
/* Success! */
return
0
;
}
#ifdef EB4231_SUPPORT
/* Attach to an cs4231 chip given its PROM node. */
static
int
eb4231_attach
(
struct
sparcaudio_driver
*
drv
,
struct
linux_ebus_device
*
edev
)
{
struct
cs4231_chip
*
cs4231_chip
;
int
len
,
err
,
nregs
;
struct
linux_prom_registers
regs
[
4
];
/* Allocate our private information structure. */
drv
->
private
=
kmalloc
(
sizeof
(
struct
cs4231_chip
),
GFP_KERNEL
);
if
(
drv
->
private
==
NULL
)
return
-
ENOMEM
;
/* Point at the information structure and initialize it. */
drv
->
ops
=
&
eb4231_ops
;
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_chip
->
input_ptr
=
cs4231_chip
->
output_ptr
=
NULL
;
cs4231_chip
->
input_size
=
cs4231_chip
->
output_size
=
0
;
cs4231_chip
->
status
=
0
;
drv
->
dev
=
(
struct
sbus_dev
*
)
edev
->
bus
->
self
;
len
=
prom_getproperty
(
edev
->
prom_node
,
"reg"
,
(
void
*
)
regs
,
sizeof
(
regs
));
if
((
len
%
sizeof
(
regs
[
0
]))
!=
0
)
{
printk
(
"eb4231: Strange reg property size %d
\n
"
,
len
);
return
-
ENODEV
;
}
nregs
=
len
/
sizeof
(
regs
[
0
]);
cs4231_chip
->
regs
=
(
unsigned
long
)
ioremap
(
edev
->
resource
[
0
].
start
,
0x10
);
cs4231_chip
->
eb2p
=
(
unsigned
long
)
ioremap
(
edev
->
resource
[
1
].
start
,
0x10
);
cs4231_chip
->
eb2c
=
(
unsigned
long
)
ioremap
(
edev
->
resource
[
2
].
start
,
0x10
);
cs4231_chip
->
status
|=
CS_STATUS_IS_EBUS
;
/* Attach the interrupt handler to the audio interrupt. */
cs4231_chip
->
irq
=
edev
->
irqs
[
0
];
cs4231_chip
->
irq2
=
edev
->
irqs
[
1
];
if
(
request_irq
(
cs4231_chip
->
irq
,
eb4231_cinterrupt
,
SA_SHIRQ
,
"cs4231"
,
drv
)
||
request_irq
(
cs4231_chip
->
irq2
,
eb4231_pinterrupt
,
SA_SHIRQ
,
"cs4231"
,
drv
))
goto
bail
;
cs4231_chip
->
nirqs
=
2
;
cs4231_enable_interrupts
(
drv
);
/* Reset the audio chip. */
cs4231_chip_reset
(
drv
);
/* Register ourselves with the midlevel audio driver. */
err
=
register_sparcaudio_driver
(
drv
,
1
);
if
(
err
<
0
)
{
bail:
printk
(
KERN_ERR
"cs4231: unable to register
\n
"
);
cs4231_disable_interrupts
(
drv
);
free_irq
(
cs4231_chip
->
irq
,
drv
);
free_irq
(
cs4231_chip
->
irq2
,
drv
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
cs4231_chip
->
perchip_info
.
play
.
active
=
cs4231_chip
->
perchip_info
.
play
.
pause
=
0
;
cs4231_chip
->
perchip_info
.
record
.
active
=
cs4231_chip
->
perchip_info
.
record
.
pause
=
0
;
cs4231_chip
->
perchip_info
.
play
.
avail_ports
=
(
AUDIO_HEADPHONE
|
AUDIO_SPEAKER
|
AUDIO_LINE_OUT
);
cs4231_chip
->
perchip_info
.
record
.
avail_ports
=
(
AUDIO_INTERNAL_CD_IN
|
AUDIO_LINE_IN
|
AUDIO_MICROPHONE
|
AUDIO_ANALOG_LOOPBACK
);
/* Announce the hardware to the user. */
printk
(
KERN_INFO
"audio%d: cs4231%c(eb2) at %lx irq %s
\n
"
,
drv
->
index
,
(
cs4231_chip
->
status
&
CS_STATUS_REV_A
)
?
'a'
:
' '
,
cs4231_chip
->
regs
,
__irq_itoa
(
cs4231_chip
->
irq
));
/* Success! */
return
0
;
}
#endif
#ifdef EB4231_SUPPORT
static
int
__init
ebus_cs4231_p
(
struct
linux_ebus_device
*
edev
)
{
if
(
!
strcmp
(
edev
->
prom_name
,
"SUNW,CS4231"
))
return
1
;
if
(
!
strcmp
(
edev
->
prom_name
,
"audio"
))
{
char
compat
[
16
];
prom_getstring
(
edev
->
prom_node
,
"compatible"
,
compat
,
sizeof
(
compat
));
compat
[
15
]
=
'\0'
;
if
(
!
strcmp
(
compat
,
"SUNW,CS4231"
))
return
1
;
}
return
0
;
}
#endif
/* Detach from an cs4231 chip given the device structure. */
static
void
__exit
cs4231_detach
(
struct
sparcaudio_driver
*
drv
)
{
struct
cs4231_chip
*
cs4231_chip
=
(
struct
cs4231_chip
*
)
drv
->
private
;
cs4231_disable_interrupts
(
drv
);
unregister_sparcaudio_driver
(
drv
,
1
);
free_irq
(
cs4231_chip
->
irq
,
drv
);
if
(
!
(
cs4231_chip
->
status
&
CS_STATUS_IS_EBUS
))
{
sbus_iounmap
(
cs4231_chip
->
regs
,
cs4231_chip
->
regs_size
);
}
else
{
#ifdef EB4231_SUPPORT
iounmap
(
cs4231_chip
->
regs
);
iounmap
(
cs4231_chip
->
eb2p
);
iounmap
(
cs4231_chip
->
eb2c
);
free_irq
(
cs4231_chip
->
irq2
,
drv
);
#endif
}
kfree
(
drv
->
private
);
}
/* Probe for the cs4231 chip and then attach the driver. */
static
int
__init
cs4231_init
(
void
)
{
struct
sbus_bus
*
sbus
;
struct
sbus_dev
*
sdev
;
#ifdef EB4231_SUPPORT
struct
linux_ebus
*
ebus
;
struct
linux_ebus_device
*
edev
;
#endif
num_drivers
=
0
;
/* Probe each SBUS for cs4231 chips. */
for_all_sbusdev
(
sdev
,
sbus
)
{
if
(
!
strcmp
(
sdev
->
prom_name
,
"SUNW,CS4231"
))
{
/* Don't go over the max number of drivers. */
if
(
num_drivers
>=
MAX_DRIVERS
)
continue
;
if
(
cs4231_attach
(
&
drivers
[
num_drivers
],
sdev
)
==
0
)
num_drivers
++
;
}
}
#ifdef EB4231_SUPPORT
for_each_ebus
(
ebus
)
{
for_each_ebusdev
(
edev
,
ebus
)
{
if
(
ebus_cs4231_p
(
edev
))
{
/* Don't go over the max number of drivers. */
if
(
num_drivers
>=
MAX_DRIVERS
)
continue
;
if
(
eb4231_attach
(
&
drivers
[
num_drivers
],
edev
)
==
0
)
num_drivers
++
;
}
}
}
#endif
/* Only return success if we found some cs4231 chips. */
return
(
num_drivers
>
0
)
?
0
:
-
EIO
;
}
static
void
__exit
cs4231_exit
(
void
)
{
register
int
i
;
for
(
i
=
0
;
i
<
num_drivers
;
i
++
)
{
cs4231_detach
(
&
drivers
[
i
]);
num_drivers
--
;
}
}
module_init
(
cs4231_init
);
module_exit
(
cs4231_exit
);
MODULE_LICENSE
(
"GPL"
);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
drivers/sbus/audio/cs4231.h
deleted
100644 → 0
View file @
2ee094ef
/* $Id: cs4231.h,v 1.13 1999/09/21 14:37:27 davem Exp $
* drivers/sbus/audio/cs4231.h
*
* Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
* Copyright (C) 1997 Derrick J. Brashear (shadow@dementia.org)
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
*/
#ifndef _CS4231_H_
#define _CS4231_H_
#include <linux/types.h>
/* According to the CS4231A data provided on CS web site and sun's includes */
#define IAR 0x00UL
/* Index Address Register */
#define IDR 0x04UL
/* Index Data Register */
#define STAT 0x08UL
/* Status Register */
#define PIOD 0x0cUL
/* PIO Data Register */
#define APCCSR 0x10UL
/* APC DMA CSR */
#define APCCVA 0x20UL
/* APC Capture DMA Address */
#define APCCC 0x24UL
/* APC Capture Count */
#define APCCNVA 0x28UL
/* APC Capture DMA Next Address */
#define APCCNC 0x2cUL
/* APC Capture Next Count */
#define APCPVA 0x30UL
/* APC Play DMA Address */
#define APCPC 0x34UL
/* APC Play Count */
#define APCPNVA 0x38UL
/* APC Play DMA Next Address */
#define APCPNC 0x3cUL
/* APC Play Next Count */
/* EBUS DMA Registers */
#define EBDMA_CSR 0x00UL
/* Control/Status */
#define EBDMA_ADDR 0x04UL
/* DMA Address */
#define EBDMA_COUNT 0x08UL
/* DMA Count */
/* Our structure for each chip */
struct
cs4231_chip
{
unsigned
long
regs
;
unsigned
long
eb2c
;
unsigned
long
eb2p
;
struct
audio_info
perchip_info
;
unsigned
int
playlen
,
reclen
;
int
irq
,
irq2
,
nirqs
;
unsigned
long
regs_size
;
/* Keep track of various info */
volatile
unsigned
int
status
;
/* Current buffer that the driver is playing. */
volatile
__u8
*
output_ptr
;
volatile
__u32
output_size
;
volatile
__u32
output_dma_handle
,
output_next_dma_handle
;
volatile
__u32
output_dma_size
,
output_next_dma_size
;
/* Current record buffer. */
volatile
__u8
*
input_ptr
;
volatile
__u32
input_size
;
volatile
__u32
input_dma_handle
,
input_next_dma_handle
;
volatile
__u32
input_dma_size
,
input_next_dma_size
;
/* Number of buffers in the pipe. */
volatile
__u32
playing_count
;
volatile
__u32
recording_count
;
};
#ifdef EB4231_SUPPORT
#define CS4231_READ32(__C, __REG) \
(((__C)->status & CS_STATUS_IS_EBUS) ? \
readl((__REG)) : \
sbus_readl((__REG)))
#define CS4231_READ8(__C, __REG) \
(((__C)->status & CS_STATUS_IS_EBUS) ? \
readb((__REG)) : \
sbus_readb((__REG)))
#define CS4231_WRITE32(__C, __REG, __VAL) \
(((__C)->status & CS_STATUS_IS_EBUS) ? \
writel((__VAL), (__REG)) : \
sbus_writel((__VAL), (__REG)))
#define CS4231_WRITE8(__C, __REG, __VAL) \
(((__C)->status & CS_STATUS_IS_EBUS) ? \
writeb((__VAL), (__REG)) : \
sbus_writeb((__VAL), (__REG)))
#else
/* We can assume all is SBUS in this case. */
#define CS4231_READ32(__C, __REG) sbus_readl((__REG))
#define CS4231_READ8(__C, __REG) sbus_readb((__REG))
#define CS4231_WRITE32(__C, __REG, __VAL) sbus_writel((__VAL), (__REG))
#define CS4231_WRITE8(__C, __REG, __VAL) sbus_writeb((__VAL), (__REG))
#endif
/* Local status bits */
#define CS_STATUS_NEED_INIT 0x01
#define CS_STATUS_INIT_ON_CLOSE 0x02
#define CS_STATUS_REV_A 0x04
#define CS_STATUS_INTS_ON 0x08
#define CS_STATUS_IS_ULTRA 0x10
#define CS_STATUS_IS_EBUS 0x20
#define CS_TIMEOUT 9000000
#define GAIN_SET(var, gain) ((var & ~(0x3f)) | gain)
#define RECGAIN_SET(var, gain) ((var & ~(0x1f)) | gain)
/* bits 0-3 set address of register accessed by idr register */
/* bit 4 allows access to idr registers 16-31 in mode 2 only */
/* bit 5 if set causes dma transfers to cease if the int bit of status set */
#define IAR_AUTOCAL_BEGIN 0x40
/* MCE */
#define IAR_NOT_READY 0x80
/* INIT */
#define IAR_AUTOCAL_END ~(IAR_AUTOCAL_BEGIN)
/* MCD */
/* Registers 1-15 modes 1 and 2. Registers 16-31 mode 2 only */
/* Registers assumed to be same in both modes unless noted */
/* 0 - Left Input Control */
/* 1 - Right Input Control */
#define MIC_ENABLE(var) ((var & 0x2f) | 0x80)
#define LINE_ENABLE(var) (var & 0x2f)
#define CDROM_ENABLE(var) ((var & 0x2f) | 0x40)
#define OUTPUTLOOP_ENABLE(var) ((var & 0x2f) | 0xC0)
#define INPUTCR_AUX1 0x40
/* 2 - Left Aux 1 Input Control */
/* 3 - Right Aux 1 Input Control */
/* 4 - Left Aux 2 Input Control */
/* 5 - Right Aux 2 Input Control */
/* 6 - Left Output Control */
/* 7 - Right Output Control */
#define OUTCR_MUTE 0x80
#define OUTCR_UNMUTE ~0x80
/* 8 - Playback Data Format (Mode 2) */
#define CHANGE_DFR(var, val) ((var & ~(0xF)) | val)
#define CHANGE_ENCODING(var, val) ((var & ~(0xe0)) | val)
#define DEFAULT_DATA_FMAT CS4231_DFR_ULAW
#define CS4231_DFR_5512 0x01
#define CS4231_DFR_6615 0x0f
#define CS4231_DFR_8000 0x00
#define CS4231_DFR_9600 0x0e
#define CS4231_DFR_11025 0x03
#define CS4231_DFR_16000 0x02
#define CS4231_DFR_18900 0x05
#define CS4231_DFR_22050 0x07
#define CS4231_DFR_27429 0x04
#define CS4231_DFR_32000 0x06
#define CS4231_DFR_33075 0x0d
#define CS4231_DFR_37800 0x09
#define CS4231_DFR_44100 0x0b
#define CS4231_DFR_48000 0x0c
#define CS4231_DFR_LINEAR8 0x00
#define CS4231_DFR_ULAW 0x20
#define CS4231_DFR_LINEARLE 0x40
#define CS4231_DFR_ALAW 0x60
#define CS4231_DFR_ADPCM 0xa0
/* N/A in mode 1 */
#define CS4231_DFR_LINEARBE 0xc0
/* N/A in mode 1 */
#define CS4231_STEREO_ON(val) (val | 0x10)
#define CS4231_MONO_ON(val) (val & ~0x10)
/* 9 - Interface Config. Register */
#define PEN_ENABLE (0x01)
/* Playback Enable */
#define PEN_DISABLE (~0x01)
#define CEN_ENABLE (0x02)
/* Capture Enable */
#define CEN_DISABLE (~0x02)
#define SDC_ENABLE (0x04)
/* Turn on single DMA Channel mode */
#define ACAL_CONV 0x08
/* Turn on converter autocal */
#define ACAL_DISABLE (~0x08)
#define ACAL_DAC 0x10
/* Turn on DAC autocal */
#define ACAL_FULL (ACAL_DAC|ACAL_CONV)
/* Turn on full autocal */
#define PPIO 0x20
/* do playback via PIO rather than DMA */
#define CPIO 0x40
/* do capture via PIO rather than DMA */
#define ICR_AUTOCAL_INIT 0x01
/* 10 - Pin Control Register */
#define INTR_ON 0x82
#define INTR_OFF 0x80
#define PINCR_LINE_MUTE 0x40
#define PINCR_HDPH_MUTE 0x80
/* 11 - Test/Initialization */
#define DRQ_STAT 0x10
#define AUTOCAL_IN_PROGRESS 0x20
/* 12 - Misc Information */
#define MISC_IR_MODE2 0x40
/* 13 - Loopback Control */
#define LOOPB_ON 0x01
#define LOOPB_OFF 0x00
/* 14 - shared play/capture upper (mode 1) */
/* 15 - shared play/capture lower (mode 1) */
/* 14 - Playback Upper (mode 2) */
/* 15 - Playback Lower (mode 2) */
/* The rest are mode 2 only */
/* 16 - Alternate Feature 1 Enable */
#define DAC_ZERO 0x01
#define PLAY_MCE 0x10
#define CAPTURE_MCE 0x20
#define TIMER_ENABLE 0x40
#define OLB_ENABLE 0x80
/* go to 2.88 vpp analog output */
/* 17 - Alternate Feature 2 Enable */
#define HPF_ON 0x01
/* High Pass Filter */
#define XTALE_ON 0x02
/* Enable both crystals */
#define APAR_OFF 0x04
/* ADPCM playback accum reset */
/* 18 - Left Line Input Gain */
/* 19 - Right Line Input Gain */
/* 20 - Timer High */
/* 21 - Timer Low */
/* 22 - unused */
/* 23 - Alt. Fea. Ena 3 */
#define ACF 0x01
/* 24 - Alternate Feature Status */
#define CS_PU 0x01
/* Underrun */
#define CS_PO 0x02
/* Overrun */
#define CS_CU 0x04
/* Underrun */
#define CS_CO 0x08
/* Overrun */
#define CS_PI 0x10
#define CS_CI 0x20
#define CS_TI 0x40
/* 25 - Version */
#define CS4231A 0x20
#define CS4231CDE 0x80
/* 26 - Mono I/O Control */
#define CHANGE_MONO_GAIN(val) ((val & ~(0xFF)) | val)
#define MONO_IOCR_BYPASS 0x20
#define MONO_IOCR_MUTE 0x40
#define MONO_IOCR_INMUTE 0x80
/* 27 - Unused */
/* 28 - Capture Data Format */
/* see register 8 */
/* 29 - Unused */
/* 30 - Capture Upper */
/* 31 - Capture Lower */
/* Following are APC CSR register definitions for the Sparc */
#define APC_INT_PENDING 0x800000
/* Interrupt Pending */
#define APC_PLAY_INT 0x400000
/* Playback interrupt */
#define APC_CAPT_INT 0x200000
/* Capture interrupt */
#define APC_GENL_INT 0x100000
/* General interrupt */
#define APC_XINT_ENA 0x80000
/* General ext int. enable */
#define APC_XINT_PLAY 0x40000
/* Playback ext intr */
#define APC_XINT_CAPT 0x20000
/* Capture ext intr */
#define APC_XINT_GENL 0x10000
/* Error ext intr */
#define APC_XINT_EMPT 0x8000
/* Pipe empty interrupt (0 write to pva) */
#define APC_XINT_PEMP 0x4000
/* Play pipe empty (pva and pnva not set) */
#define APC_XINT_PNVA 0x2000
/* Playback NVA dirty */
#define APC_XINT_PENA 0x1000
/* play pipe empty Int enable */
#define APC_XINT_COVF 0x800
/* Cap data dropped on floor */
#define APC_XINT_CNVA 0x400
/* Capture NVA dirty */
#define APC_XINT_CEMP 0x200
/* Capture pipe empty (cva and cnva not set) */
#define APC_XINT_CENA 0x100
/* Cap. pipe empty int enable */
#define APC_PPAUSE 0x80
/* Pause the play DMA */
#define APC_CPAUSE 0x40
/* Pause the capture DMA */
#define APC_CDC_RESET 0x20
/* CODEC RESET */
#define APC_PDMA_READY 0x08
/* Play DMA Go */
#define APC_CDMA_READY 0x04
/* Capture DMA Go */
#define APC_CHIP_RESET 0x01
/* Reset the chip */
#define APC_INIT_SETUP (APC_CDMA_READY | APC_PDMA_READY | APC_XINT_ENA | \
APC_XINT_PLAY | APC_XINT_GENL | APC_INT_PENDING | \
APC_PLAY_INT | APC_CAPT_INT | APC_GENL_INT)
#define APC_PLAY_SETUP (APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA | \
APC_XINT_PLAY | APC_XINT_EMPT | APC_XINT_GENL | \
APC_XINT_PENA | APC_PDMA_READY)
#define APC_CAPT_SETUP (APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA | \
APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL | \
APC_CDMA_READY)
/* Following are EB2 CSR register definitions for the Sparc */
/* asm/ebus.h has the base settings */
#define EB2_PLAY_SETUP (EBUS_DCSR_BURST_SZ_8 | EBUS_DCSR_INT_EN | EBUS_DCSR_EN_DMA | \
EBUS_DCSR_EN_CNT | EBUS_DCSR_TC)
#define EB2_CAPT_SETUP (EBUS_DCSR_BURST_SZ_8 | EBUS_DCSR_INT_EN | EBUS_DCSR_EN_DMA| \
EBUS_DCSR_EN_CNT | EBUS_DCSR_TC | EBUS_DCSR_WRITE)
#define CS4231_MIN_ATEN (0)
#define CS4231_MAX_ATEN (31)
#define CS4231_MAX_DEV_ATEN (63)
#define CS4231_MON_MIN_ATEN (0)
#define CS4231_MON_MAX_ATEN (63)
#define CS4231_DEFAULT_PLAYGAIN (132)
#define CS4231_DEFAULT_RECGAIN (126)
#define CS4231_MIN_GAIN (0)
#define CS4231_MAX_GAIN (15)
#define CS4231_PRECISION (8)
/* # of bits/sample */
#define CS4231_CHANNELS (1)
/* channels/sample */
#define CS4231_RATE (8000)
/* default sample rate */
#endif
/* _CS4231_H_ */
drivers/sbus/audio/dbri.c
deleted
100644 → 0
View file @
2ee094ef
/* $Id: dbri.c,v 1.27 2001/10/08 22:19:50 davem Exp $
* drivers/sbus/audio/dbri.c
*
* Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
* Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org)
*
* This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO
* on Sun SPARCstation 10, 20, LX and Voyager models.
*
* - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel
* data time multiplexer with ISDN support (aka T7259)
* Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel.
* CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?).
* Documentation:
* - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from
* Sparc Technology Business (courtesy of Sun Support)
* - Data sheet of the T7903, a newer but very similar ISA bus equivalent
* available from the Lucent (formarly AT&T microelectronics) home
* page.
* - http://www.freesoft.org/Linux/DBRI/
* - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec
* Interfaces: CHI, Audio In & Out, 2 bits parallel
* Documentation: from the Crystal Semiconductor home page.
*
* The DBRI is a 32 pipe machine, each pipe can transfer some bits between
* memory and a serial device (long pipes, nr 0-15) or between two serial
* devices (short pipes, nr 16-31), or simply send a fixed data to a serial
* device (short pipes).
* A timeslot defines the bit-offset and nr of bits read from a serial device.
* The timeslots are linked to 6 circular lists, one for each direction for
* each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes
* (the second one is a monitor/tee pipe, valid only for serial input).
*
* The mmcodec is connected via the CHI bus and needs the data & some
* parameters (volume, balance, output selection) timemultiplexed in 8 byte
* chunks. It also has a control mode, which serves for audio format setting.
*
* Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on
* the same CHI bus, so I thought perhaps it is possible to use the onboard
* & the speakerbox codec simultanously, giving 2 (not very independent :-)
* audio devices. But the SUN HW group decided against it, at least on my
* LX the speakerbox connector has at least 1 pin missing and 1 wrongly
* connected.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/sbus.h>
#include <asm/pgtable.h>
#include <asm/audioio.h>
#include "dbri.h"
#if defined(DBRI_ISDN)
#include "../../isdn/hisax/hisax.h"
#include "../../isdn/hisax/isdnl1.h"
#include "../../isdn/hisax/foreign.h"
#endif
#define DBRI_DEBUG
#ifdef DBRI_DEBUG
#define dprintk(a, x) if(dbri_debug & a) printk x
#define D_GEN (1<<0)
#define D_INT (1<<1)
#define D_CMD (1<<2)
#define D_MM (1<<3)
#define D_USR (1<<4)
#define D_DESC (1<<5)
static
int
dbri_debug
=
0
;
MODULE_PARM
(
dbri_debug
,
"i"
);
static
int
dbri_trace
=
0
;
MODULE_PARM
(
dbri_trace
,
"i"
);
#define tprintk(x) if(dbri_trace) printk x
static
char
*
cmds
[]
=
{
"WAIT"
,
"PAUSE"
,
"JUMP"
,
"IIQ"
,
"REX"
,
"SDP"
,
"CDP"
,
"DTS"
,
"SSP"
,
"CHI"
,
"NT"
,
"TE"
,
"CDEC"
,
"TEST"
,
"CDM"
,
"RESRV"
};
#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
#else
#define dprintk(a, x)
#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
#endif
/* DBRI_DEBUG */
#define MAX_DRIVERS 2
/* Increase this if need more than 2 DBRI's */
static
struct
sparcaudio_driver
drivers
[
MAX_DRIVERS
];
static
int
num_drivers
=
0
;
/*
****************************************************************************
************** DBRI initialization and command synchronization *************
****************************************************************************
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 in parallel with 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 pointer into dbri->dma->cmd
in return. After the commands have been writen, dbri_cmdsend() is
called with the final pointer value.
Something a little more clever is required if this code is ever run
on an SMP machine.
*/
static
int
dbri_locked
=
0
;
static
volatile
s32
*
dbri_cmdlock
(
struct
dbri
*
dbri
)
{
if
(
dbri_locked
)
printk
(
"DBRI: Command buffer locked! (bug in driver)
\n
"
);
dbri_locked
++
;
return
&
dbri
->
dma
->
cmd
[
0
];
}
static
void
dbri_process_interrupt_buffer
(
struct
dbri
*
);
static
void
dbri_cmdsend
(
struct
dbri
*
dbri
,
volatile
s32
*
cmd
)
{
int
MAXLOOPS
=
1000000
;
int
maxloops
=
MAXLOOPS
;
unsigned
long
flags
;
volatile
s32
*
ptr
;
for
(
ptr
=
&
dbri
->
dma
->
cmd
[
0
];
ptr
<
cmd
;
ptr
++
)
{
dprintk
(
D_CMD
,
(
"DBRI cmd: %lx:%08x
\n
"
,
(
unsigned
long
)
ptr
,
*
ptr
));
}
save_and_cli
(
flags
);
dbri_locked
--
;
if
(
dbri_locked
!=
0
)
{
printk
(
"DBRI: Command buffer improperly locked! (bug in driver)
\n
"
);
}
else
if
((
cmd
-
&
dbri
->
dma
->
cmd
[
0
])
>=
DBRI_NO_CMDS
-
1
)
{
printk
(
"DBRI: Command buffer overflow! (bug in driver)
\n
"
);
}
else
{
*
(
cmd
++
)
=
DBRI_CMD
(
D_PAUSE
,
0
,
0
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_WAIT
,
1
,
0
);
dbri
->
wait_seen
=
0
;
sbus_writel
(
dbri
->
dma_dvma
,
dbri
->
regs
+
REG8
);
while
((
--
maxloops
)
>
0
&&
(
sbus_readl
(
dbri
->
regs
+
REG0
)
&
D_P
))
barrier
();
if
(
maxloops
==
0
)
{
printk
(
"DBRI: Chip never completed command buffer
\n
"
);
}
else
{
while
((
--
maxloops
)
>
0
&&
(
!
dbri
->
wait_seen
))
dbri_process_interrupt_buffer
(
dbri
);
if
(
maxloops
==
0
)
{
printk
(
"DBRI: Chip never acked WAIT
\n
"
);
}
else
{
dprintk
(
D_INT
,
(
"DBRI: Chip completed command "
"buffer (%d)
\n
"
,
MAXLOOPS
-
maxloops
));
}
}
}
restore_flags
(
flags
);
}
static
void
dbri_reset
(
struct
dbri
*
dbri
)
{
int
i
;
dprintk
(
D_GEN
,
(
"DBRI: reset 0:%x 2:%x 8:%x 9:%x
\n
"
,
sbus_readl
(
dbri
->
regs
+
REG0
),
sbus_readl
(
dbri
->
regs
+
REG2
),
sbus_readl
(
dbri
->
regs
+
REG8
),
sbus_readl
(
dbri
->
regs
+
REG9
)));
sbus_writel
(
D_R
,
dbri
->
regs
+
REG0
);
/* Soft Reset */
for
(
i
=
0
;
(
sbus_readl
(
dbri
->
regs
+
REG0
)
&
D_R
)
&&
i
<
64
;
i
++
)
udelay
(
10
);
}
static
void
dbri_detach
(
struct
dbri
*
dbri
)
{
dbri_reset
(
dbri
);
free_irq
(
dbri
->
irq
,
dbri
);
sbus_iounmap
(
dbri
->
regs
,
dbri
->
regs_size
);
sbus_free_consistent
(
dbri
->
sdev
,
sizeof
(
struct
dbri_dma
),
(
void
*
)
dbri
->
dma
,
dbri
->
dma_dvma
);
kfree
(
dbri
);
}
static
void
dbri_initialize
(
struct
dbri
*
dbri
)
{
volatile
s32
*
cmd
;
u32
dma_addr
,
tmp
;
int
n
;
dbri_reset
(
dbri
);
dprintk
(
D_GEN
,
(
"DBRI: init: cmd: %p, int: %p
\n
"
,
&
dbri
->
dma
->
cmd
[
0
],
&
dbri
->
dma
->
intr
[
0
]));
/*
* Initialize the interrupt ringbuffer.
*/
for
(
n
=
0
;
n
<
DBRI_NO_INTS
-
1
;
n
++
)
{
dma_addr
=
dbri
->
dma_dvma
;
dma_addr
+=
dbri_dma_off
(
intr
,
((
n
+
1
)
&
DBRI_INT_BLK
));
dbri
->
dma
->
intr
[
n
*
DBRI_INT_BLK
]
=
dma_addr
;
}
dma_addr
=
dbri
->
dma_dvma
+
dbri_dma_off
(
intr
,
0
);
dbri
->
dma
->
intr
[
n
*
DBRI_INT_BLK
]
=
dma_addr
;
dbri
->
dbri_irqp
=
1
;
/* We should query the openprom to see what burst sizes this
* SBus supports. For now, just disable all SBus bursts */
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
&=
~
(
D_G
|
D_S
|
D_E
);
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
/*
* Set up the interrupt queue
*/
cmd
=
dbri_cmdlock
(
dbri
);
dma_addr
=
dbri
->
dma_dvma
+
dbri_dma_off
(
intr
,
0
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_IIQ
,
0
,
0
);
*
(
cmd
++
)
=
dma_addr
;
dbri_cmdsend
(
dbri
,
cmd
);
}
/*
****************************************************************************
*************************** DBRI interrupt handler *************************
****************************************************************************
The DBRI communicates with the CPU mainly via a circular interrupt
buffer. When an interrupt is signaled, the CPU walks through the
buffer and calls dbri_process_one_interrupt() for each interrupt word.
Complicated interrupts are handled by dedicated functions (which
appear first in this file). Any pending interrupts can be serviced by
calling dbri_process_interrupt_buffer(), which works even if the CPU's
interrupts are disabled. This function is used by dbri_cmdsend()
to make sure we're synced up with the chip after each command sequence,
even if we're running cli'ed.
*/
/*
* Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr.
* So we have to reverse the bits. Note: not all bit lengths are supported
*/
static
__u32
reverse_bytes
(
__u32
b
,
int
len
)
{
switch
(
len
)
{
case
32
:
b
=
((
b
&
0xffff0000
)
>>
16
)
|
((
b
&
0x0000ffff
)
<<
16
);
case
16
:
b
=
((
b
&
0xff00ff00
)
>>
8
)
|
((
b
&
0x00ff00ff
)
<<
8
);
case
8
:
b
=
((
b
&
0xf0f0f0f0
)
>>
4
)
|
((
b
&
0x0f0f0f0f
)
<<
4
);
case
4
:
b
=
((
b
&
0xcccccccc
)
>>
2
)
|
((
b
&
0x33333333
)
<<
2
);
case
2
:
b
=
((
b
&
0xaaaaaaaa
)
>>
1
)
|
((
b
&
0x55555555
)
<<
1
);
case
1
:
case
0
:
break
;
default:
printk
(
"DBRI reverse_bytes: unsupported length
\n
"
);
};
return
b
;
}
/* transmission_complete_intr()
*
* Called by main interrupt handler when DBRI signals transmission complete
* on a pipe (interrupt triggered by the B bit in a transmit descriptor).
*
* Walks through the pipe's list of transmit buffer descriptors, releasing
* each one's DMA buffer (if present), flagging the descriptor available,
* and signaling its callback routine (if present), before proceeding
* to the next one. Stops when the first descriptor is found without
* TBC (Transmit Buffer Complete) set, or we've run through them all.
*/
static
void
transmission_complete_intr
(
struct
dbri
*
dbri
,
int
pipe
)
{
int
td
;
int
status
;
void
*
buffer
;
void
(
*
callback
)(
void
*
,
int
);
void
*
callback_arg
;
td
=
dbri
->
pipes
[
pipe
].
desc
;
while
(
td
>=
0
)
{
if
(
td
>=
DBRI_NO_DESCS
)
{
printk
(
"DBRI: invalid td on pipe %d
\n
"
,
pipe
);
return
;
}
status
=
DBRI_TD_STATUS
(
dbri
->
dma
->
desc
[
td
].
word4
);
if
(
!
(
status
&
DBRI_TD_TBC
))
{
break
;
}
dprintk
(
D_INT
,
(
"DBRI: TD %d, status 0x%02x
\n
"
,
td
,
status
));
buffer
=
dbri
->
descs
[
td
].
buffer
;
if
(
buffer
)
sbus_unmap_single
(
dbri
->
sdev
,
dbri
->
descs
[
td
].
buffer_dvma
,
dbri
->
descs
[
td
].
len
,
SBUS_DMA_TODEVICE
);
callback
=
dbri
->
descs
[
td
].
output_callback
;
callback_arg
=
dbri
->
descs
[
td
].
output_callback_arg
;
dbri
->
descs
[
td
].
inuse
=
0
;
td
=
dbri
->
descs
[
td
].
next
;
dbri
->
pipes
[
pipe
].
desc
=
td
;
if
(
callback
!=
NULL
)
callback
(
callback_arg
,
status
&
0xe
);
}
}
static
void
reception_complete_intr
(
struct
dbri
*
dbri
,
int
pipe
)
{
int
rd
=
dbri
->
pipes
[
pipe
].
desc
;
s32
status
;
void
*
buffer
;
void
(
*
callback
)(
void
*
,
int
,
unsigned
int
);
if
(
rd
<
0
||
rd
>=
DBRI_NO_DESCS
)
{
printk
(
"DBRI: invalid rd on pipe %d
\n
"
,
pipe
);
return
;
}
dbri
->
descs
[
rd
].
inuse
=
0
;
dbri
->
pipes
[
pipe
].
desc
=
dbri
->
descs
[
rd
].
next
;
status
=
dbri
->
dma
->
desc
[
rd
].
word1
;
buffer
=
dbri
->
descs
[
rd
].
buffer
;
if
(
buffer
)
sbus_unmap_single
(
dbri
->
sdev
,
dbri
->
descs
[
rd
].
buffer_dvma
,
dbri
->
descs
[
rd
].
len
,
SBUS_DMA_FROMDEVICE
);
callback
=
dbri
->
descs
[
rd
].
input_callback
;
if
(
callback
!=
NULL
)
callback
(
dbri
->
descs
[
rd
].
input_callback_arg
,
DBRI_RD_STATUS
(
status
),
DBRI_RD_CNT
(
status
)
-
2
);
dprintk
(
D_INT
,
(
"DBRI: Recv RD %d, status 0x%02x, len %d
\n
"
,
rd
,
DBRI_RD_STATUS
(
status
),
DBRI_RD_CNT
(
status
)));
}
static
void
dbri_process_one_interrupt
(
struct
dbri
*
dbri
,
int
x
)
{
int
val
=
D_INTR_GETVAL
(
x
);
int
channel
=
D_INTR_GETCHAN
(
x
);
int
command
=
D_INTR_GETCMD
(
x
);
int
code
=
D_INTR_GETCODE
(
x
);
int
rval
=
D_INTR_GETRVAL
(
x
);
if
(
channel
==
D_INTR_CMD
)
{
dprintk
(
D_INT
,(
"DBRI: INTR: Command: %-5s Value:%d
\n
"
,
cmds
[
command
],
val
));
}
else
{
dprintk
(
D_INT
,(
"DBRI: INTR: Chan:%d Code:%d Val:%#x
\n
"
,
channel
,
code
,
rval
));
}
if
(
channel
==
D_INTR_CMD
&&
command
==
D_WAIT
)
dbri
->
wait_seen
++
;
if
(
code
==
D_INTR_SBRI
)
{
/* SBRI - BRI status change */
const
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
);
}
if
(
code
==
D_INTR_BRDY
)
reception_complete_intr
(
dbri
,
channel
);
if
(
code
==
D_INTR_XCMP
)
transmission_complete_intr
(
dbri
,
channel
);
if
(
code
==
D_INTR_UNDR
)
{
/* UNDR - Transmission underrun
* resend SDP command with clear pipe bit (C) set
*/
volatile
s32
*
cmd
;
int
pipe
=
channel
;
int
td
=
dbri
->
pipes
[
pipe
].
desc
;
dbri
->
dma
->
desc
[
td
].
word4
=
0
;
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_SDP
,
0
,
dbri
->
pipes
[
pipe
].
sdp
|
D_SDP_P
|
D_SDP_C
|
D_SDP_2SAME
);
*
(
cmd
++
)
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
td
);
dbri_cmdsend
(
dbri
,
cmd
);
}
if
(
code
==
D_INTR_FXDT
)
{
/* FXDT - Fixed data change */
if
(
dbri
->
pipes
[
channel
].
sdp
&
D_SDP_MSB
)
val
=
reverse_bytes
(
val
,
dbri
->
pipes
[
channel
].
length
);
if
(
dbri
->
pipes
[
channel
].
recv_fixed_ptr
)
*
(
dbri
->
pipes
[
channel
].
recv_fixed_ptr
)
=
val
;
}
}
/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
* buffer until it finds a zero word (indicating nothing more to do
* right now). Non-zero words require processing and are handed off
* to dbri_process_one_interrupt AFTER advancing the pointer. This
* order is important since we might recurse back into this function
* and need to make sure the pointer has been advanced first.
*/
static
void
dbri_process_interrupt_buffer
(
struct
dbri
*
dbri
)
{
s32
x
;
while
((
x
=
dbri
->
dma
->
intr
[
dbri
->
dbri_irqp
])
!=
0
)
{
dbri
->
dma
->
intr
[
dbri
->
dbri_irqp
]
=
0
;
dbri
->
dbri_irqp
++
;
if
(
dbri
->
dbri_irqp
==
(
DBRI_NO_INTS
*
DBRI_INT_BLK
))
dbri
->
dbri_irqp
=
1
;
else
if
((
dbri
->
dbri_irqp
&
(
DBRI_INT_BLK
-
1
))
==
0
)
dbri
->
dbri_irqp
++
;
tprintk
((
"dbri->dbri_irqp == %d
\n
"
,
dbri
->
dbri_irqp
));
dbri_process_one_interrupt
(
dbri
,
x
);
}
}
static
void
dbri_intr
(
int
irq
,
void
*
opaque
,
struct
pt_regs
*
regs
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
opaque
;
int
x
;
/*
* Read it, so the interrupt goes away.
*/
x
=
sbus_readl
(
dbri
->
regs
+
REG1
);
dprintk
(
D_INT
,
(
"DBRI: Interrupt! (reg1=0x%08x)
\n
"
,
x
));
if
(
x
&
(
D_MRR
|
D_MLE
|
D_LBG
|
D_MBE
))
{
u32
tmp
;
if
(
x
&
D_MRR
)
printk
(
"DBRI: Multiple Error Ack on SBus
\n
"
);
if
(
x
&
D_MLE
)
printk
(
"DBRI: Multiple Late Error on SBus
\n
"
);
if
(
x
&
D_LBG
)
printk
(
"DBRI: Lost Bus Grant on SBus
\n
"
);
if
(
x
&
D_MBE
)
printk
(
"DBRI: Burst Error on SBus
\n
"
);
/* Some of these SBus errors cause the chip's SBus circuitry
* to be disabled, so just re-enable and try to keep going.
*
* The only one I've seen is MRR, which will be triggered
* if you let a transmit pipe underrun, then try to CDP it.
*
* If these things persist, we should probably reset
* and re-init the chip.
*/
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
&=
~
(
D_D
);
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
}
#if 0
if (!(x & D_IR)) /* Not for us */
return;
#endif
dbri_process_interrupt_buffer
(
dbri
);
}
/*
****************************************************************************
************************** DBRI data pipe management ***********************
****************************************************************************
While DBRI control functions use the command and interrupt buffers, the
main data path takes the form of data pipes, which can be short (command
and interrupt driven), or long (attached to DMA buffers). These functions
provide a rudimentary means of setting up and managing the DBRI's pipes,
but the calling functions have to make sure they respect the pipes' linked
list ordering, among other things. The transmit and receive functions
here interface closely with the transmit and receive interrupt code.
*/
static
int
pipe_active
(
struct
dbri
*
dbri
,
int
pipe
)
{
return
(
dbri
->
pipes
[
pipe
].
desc
!=
-
1
);
}
/* reset_pipe(dbri, pipe)
*
* Called on an in-use pipe to clear anything being transmitted or received
*/
static
void
reset_pipe
(
struct
dbri
*
dbri
,
int
pipe
)
{
int
sdp
;
int
desc
;
volatile
int
*
cmd
;
if
(
pipe
<
0
||
pipe
>
31
)
{
printk
(
"DBRI: reset_pipe called with illegal pipe number
\n
"
);
return
;
}
sdp
=
dbri
->
pipes
[
pipe
].
sdp
;
if
(
sdp
==
0
)
{
printk
(
"DBRI: reset_pipe called on uninitialized pipe
\n
"
);
return
;
}
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_SDP
,
0
,
sdp
|
D_SDP_C
|
D_SDP_P
);
*
(
cmd
++
)
=
0
;
dbri_cmdsend
(
dbri
,
cmd
);
desc
=
dbri
->
pipes
[
pipe
].
desc
;
while
(
desc
!=
-
1
)
{
void
*
buffer
=
dbri
->
descs
[
desc
].
buffer
;
void
(
*
output_callback
)
(
void
*
,
int
)
=
dbri
->
descs
[
desc
].
output_callback
;
void
*
output_callback_arg
=
dbri
->
descs
[
desc
].
output_callback_arg
;
void
(
*
input_callback
)
(
void
*
,
int
,
unsigned
int
)
=
dbri
->
descs
[
desc
].
input_callback
;
void
*
input_callback_arg
=
dbri
->
descs
[
desc
].
input_callback_arg
;
if
(
buffer
)
sbus_unmap_single
(
dbri
->
sdev
,
dbri
->
descs
[
desc
].
buffer_dvma
,
dbri
->
descs
[
desc
].
len
,
output_callback
!=
NULL
?
SBUS_DMA_TODEVICE
:
SBUS_DMA_FROMDEVICE
);
dbri
->
descs
[
desc
].
inuse
=
0
;
desc
=
dbri
->
descs
[
desc
].
next
;
if
(
output_callback
)
output_callback
(
output_callback_arg
,
-
1
);
if
(
input_callback
)
input_callback
(
input_callback_arg
,
-
1
,
0
);
}
dbri
->
pipes
[
pipe
].
desc
=
-
1
;
}
static
void
setup_pipe
(
struct
dbri
*
dbri
,
int
pipe
,
int
sdp
)
{
if
(
pipe
<
0
||
pipe
>
31
)
{
printk
(
"DBRI: setup_pipe called with illegal pipe number
\n
"
);
return
;
}
if
((
sdp
&
0xf800
)
!=
sdp
)
{
printk
(
"DBRI: setup_pipe called with strange SDP value
\n
"
);
/* sdp &= 0xf800; */
}
/* If this is a fixed receive pipe, arrange for an interrupt
* every time its data changes
*/
if
(
D_SDP_MODE
(
sdp
)
==
D_SDP_FIXED
&&
!
(
sdp
&
D_SDP_TO_SER
))
sdp
|=
D_SDP_CHANGE
;
sdp
|=
D_PIPE
(
pipe
);
dbri
->
pipes
[
pipe
].
sdp
=
sdp
;
dbri
->
pipes
[
pipe
].
desc
=
-
1
;
reset_pipe
(
dbri
,
pipe
);
}
static
void
link_time_slot
(
struct
dbri
*
dbri
,
int
pipe
,
enum
in_or_out
direction
,
int
basepipe
,
int
length
,
int
cycle
)
{
volatile
s32
*
cmd
;
int
val
;
int
prevpipe
;
int
nextpipe
;
if
(
pipe
<
0
||
pipe
>
31
||
basepipe
<
0
||
basepipe
>
31
)
{
printk
(
"DBRI: link_time_slot called with illegal pipe number
\n
"
);
return
;
}
if
(
dbri
->
pipes
[
pipe
].
sdp
==
0
||
dbri
->
pipes
[
basepipe
].
sdp
==
0
)
{
printk
(
"DBRI: link_time_slot called on uninitialized pipe
\n
"
);
return
;
}
/* Deal with CHI special case:
* "If transmission on edges 0 or 1 is desired, then cycle n
* (where n = # of bit times per frame...) must be used."
* - DBRI data sheet, page 11
*/
if
(
basepipe
==
16
&&
direction
==
PIPEoutput
&&
cycle
==
0
)
cycle
=
dbri
->
chi_bpf
;
if
(
basepipe
==
pipe
)
{
prevpipe
=
pipe
;
nextpipe
=
pipe
;
}
else
{
/* We're not initializing a new linked list (basepipe != pipe),
* so run through the linked list and find where this pipe
* should be sloted in, based on its cycle. CHI confuses
* things a bit, since it has a single anchor for both its
* transmit and receive lists.
*/
if
(
basepipe
==
16
)
{
if
(
direction
==
PIPEinput
)
{
prevpipe
=
dbri
->
chi_in_pipe
;
}
else
{
prevpipe
=
dbri
->
chi_out_pipe
;
}
}
else
{
prevpipe
=
basepipe
;
}
nextpipe
=
dbri
->
pipes
[
prevpipe
].
nextpipe
;
while
(
dbri
->
pipes
[
nextpipe
].
cycle
<
cycle
&&
dbri
->
pipes
[
nextpipe
].
nextpipe
!=
basepipe
)
{
prevpipe
=
nextpipe
;
nextpipe
=
dbri
->
pipes
[
nextpipe
].
nextpipe
;
}
}
if
(
prevpipe
==
16
)
{
if
(
direction
==
PIPEinput
)
{
dbri
->
chi_in_pipe
=
pipe
;
}
else
{
dbri
->
chi_out_pipe
=
pipe
;
}
}
else
{
dbri
->
pipes
[
prevpipe
].
nextpipe
=
pipe
;
}
dbri
->
pipes
[
pipe
].
nextpipe
=
nextpipe
;
dbri
->
pipes
[
pipe
].
cycle
=
cycle
;
dbri
->
pipes
[
pipe
].
length
=
length
;
cmd
=
dbri_cmdlock
(
dbri
);
if
(
direction
==
PIPEinput
)
{
val
=
D_DTS_VI
|
D_DTS_INS
|
D_DTS_PRVIN
(
prevpipe
)
|
pipe
;
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
D_TS_LEN
(
length
)
|
D_TS_CYCLE
(
cycle
)
|
D_TS_NEXT
(
nextpipe
);
*
(
cmd
++
)
=
0
;
}
else
{
val
=
D_DTS_VO
|
D_DTS_INS
|
D_DTS_PRVOUT
(
prevpipe
)
|
pipe
;
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
0
;
*
(
cmd
++
)
=
D_TS_LEN
(
length
)
|
D_TS_CYCLE
(
cycle
)
|
D_TS_NEXT
(
nextpipe
);
}
dbri_cmdsend
(
dbri
,
cmd
);
}
/* I don't use this function, so it's basically untested. */
static
void
unlink_time_slot
(
struct
dbri
*
dbri
,
int
pipe
,
enum
in_or_out
direction
,
int
prevpipe
,
int
nextpipe
)
{
volatile
s32
*
cmd
;
int
val
;
if
(
pipe
<
0
||
pipe
>
31
||
prevpipe
<
0
||
prevpipe
>
31
)
{
printk
(
"DBRI: unlink_time_slot called with illegal pipe number
\n
"
);
return
;
}
cmd
=
dbri_cmdlock
(
dbri
);
if
(
direction
==
PIPEinput
)
{
val
=
D_DTS_VI
|
D_DTS_DEL
|
D_DTS_PRVIN
(
prevpipe
)
|
pipe
;
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
D_TS_NEXT
(
nextpipe
);
*
(
cmd
++
)
=
0
;
}
else
{
val
=
D_DTS_VO
|
D_DTS_DEL
|
D_DTS_PRVOUT
(
prevpipe
)
|
pipe
;
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
0
;
*
(
cmd
++
)
=
D_TS_NEXT
(
nextpipe
);
}
dbri_cmdsend
(
dbri
,
cmd
);
}
/* xmit_fixed() / recv_fixed()
*
* Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
* expected to change much, and which we don't need to buffer.
* The DBRI only interrupts us when the data changes (receive pipes),
* or only changes the data when this function is called (transmit pipes).
* Only short pipes (numbers 16-31) can be used in fixed data mode.
*
* These function operate on a 32-bit field, no matter how large
* the actual time slot is. The interrupt handler takes care of bit
* ordering and alignment. An 8-bit time slot will always end up
* in the low-order 8 bits, filled either MSB-first or LSB-first,
* depending on the settings passed to setup_pipe()
*/
static
void
xmit_fixed
(
struct
dbri
*
dbri
,
int
pipe
,
unsigned
int
data
)
{
volatile
s32
*
cmd
;
if
(
pipe
<
16
||
pipe
>
31
)
{
printk
(
"DBRI: xmit_fixed: Illegal pipe number
\n
"
);
return
;
}
if
(
D_SDP_MODE
(
dbri
->
pipes
[
pipe
].
sdp
)
==
0
)
{
printk
(
"DBRI: xmit_fixed: Uninitialized pipe %d
\n
"
,
pipe
);
return
;
}
if
(
D_SDP_MODE
(
dbri
->
pipes
[
pipe
].
sdp
)
!=
D_SDP_FIXED
)
{
printk
(
"DBRI: xmit_fixed: Non-fixed pipe %d
\n
"
,
pipe
);
return
;
}
if
(
!
(
dbri
->
pipes
[
pipe
].
sdp
&
D_SDP_TO_SER
))
{
printk
(
"DBRI: xmit_fixed: Called on receive pipe %d
\n
"
,
pipe
);
return
;
}
/* DBRI short pipes always transmit LSB first */
if
(
dbri
->
pipes
[
pipe
].
sdp
&
D_SDP_MSB
)
data
=
reverse_bytes
(
data
,
dbri
->
pipes
[
pipe
].
length
);
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_SSP
,
0
,
pipe
);
*
(
cmd
++
)
=
data
;
dbri_cmdsend
(
dbri
,
cmd
);
}
static
void
recv_fixed
(
struct
dbri
*
dbri
,
int
pipe
,
volatile
__u32
*
ptr
)
{
if
(
pipe
<
16
||
pipe
>
31
)
{
printk
(
"DBRI: recv_fixed called with illegal pipe number
\n
"
);
return
;
}
if
(
D_SDP_MODE
(
dbri
->
pipes
[
pipe
].
sdp
)
!=
D_SDP_FIXED
)
{
printk
(
"DBRI: recv_fixed called on non-fixed pipe %d
\n
"
,
pipe
);
return
;
}
if
(
dbri
->
pipes
[
pipe
].
sdp
&
D_SDP_TO_SER
)
{
printk
(
"DBRI: recv_fixed called on transmit pipe %d
\n
"
,
pipe
);
return
;
}
dbri
->
pipes
[
pipe
].
recv_fixed_ptr
=
ptr
;
}
/* xmit_on_pipe() / recv_on_pipe()
*
* Transmit/receive data on a "long" pipe - i.e, one associated
* with a DMA buffer.
*
* Only pipe numbers 0-15 can be used in this mode.
*
* Both functions take pointer/len arguments pointing to a data buffer,
* and both provide callback functions (may be NULL) to notify higher
* level code when transmission/reception is complete.
*
* Both work by building chains of descriptors which identify the
* data buffers. Buffers too large for a single descriptor will
* be spread across multiple descriptors.
*/
static
void
xmit_on_pipe
(
struct
dbri
*
dbri
,
int
pipe
,
void
*
buffer
,
unsigned
int
len
,
void
(
*
callback
)(
void
*
,
int
),
void
*
callback_arg
)
{
volatile
s32
*
cmd
;
unsigned
long
flags
;
int
td
=
0
;
int
first_td
=
-
1
;
int
last_td
=
-
1
;
__u32
dvma_buffer
,
dvma_buffer_base
;
if
(
pipe
<
0
||
pipe
>
15
)
{
printk
(
"DBRI: xmit_on_pipe: Illegal pipe number
\n
"
);
return
;
}
if
(
dbri
->
pipes
[
pipe
].
sdp
==
0
)
{
printk
(
"DBRI: xmit_on_pipe: Uninitialized pipe %d
\n
"
,
pipe
);
return
;
}
if
(
!
(
dbri
->
pipes
[
pipe
].
sdp
&
D_SDP_TO_SER
))
{
printk
(
"DBRI: xmit_on_pipe: Called on receive pipe %d
\n
"
,
pipe
);
return
;
}
dvma_buffer_base
=
dvma_buffer
=
sbus_map_single
(
dbri
->
sdev
,
buffer
,
len
,
SBUS_DMA_TODEVICE
);
while
(
len
>
0
)
{
int
mylen
;
for
(;
td
<
DBRI_NO_DESCS
;
td
++
)
{
if
(
!
dbri
->
descs
[
td
].
inuse
)
break
;
}
if
(
td
==
DBRI_NO_DESCS
)
{
printk
(
"DBRI: xmit_on_pipe: No descriptors
\n
"
);
break
;
}
if
(
len
>
((
1
<<
13
)
-
1
))
{
mylen
=
(
1
<<
13
)
-
1
;
}
else
{
mylen
=
len
;
}
dbri
->
descs
[
td
].
inuse
=
1
;
dbri
->
descs
[
td
].
next
=
-
1
;
dbri
->
descs
[
td
].
buffer
=
NULL
;
dbri
->
descs
[
td
].
output_callback
=
NULL
;
dbri
->
descs
[
td
].
input_callback
=
NULL
;
dbri
->
dma
->
desc
[
td
].
word1
=
DBRI_TD_CNT
(
mylen
);
dbri
->
dma
->
desc
[
td
].
ba
=
dvma_buffer
;
dbri
->
dma
->
desc
[
td
].
nda
=
0
;
dbri
->
dma
->
desc
[
td
].
word4
=
0
;
if
(
first_td
==
-
1
)
{
first_td
=
td
;
}
else
{
dbri
->
descs
[
last_td
].
next
=
td
;
dbri
->
dma
->
desc
[
last_td
].
nda
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
td
);
}
last_td
=
td
;
dvma_buffer
+=
mylen
;
len
-=
mylen
;
}
if
(
first_td
==
-
1
||
last_td
==
-
1
)
{
sbus_unmap_single
(
dbri
->
sdev
,
dvma_buffer_base
,
dvma_buffer
-
dvma_buffer_base
+
len
,
SBUS_DMA_TODEVICE
);
return
;
}
dbri
->
dma
->
desc
[
last_td
].
word1
|=
DBRI_TD_I
|
DBRI_TD_F
|
DBRI_TD_B
;
dbri
->
descs
[
last_td
].
buffer
=
buffer
;
dbri
->
descs
[
last_td
].
buffer_dvma
=
dvma_buffer_base
;
dbri
->
descs
[
last_td
].
len
=
dvma_buffer
-
dvma_buffer_base
+
len
;
dbri
->
descs
[
last_td
].
output_callback
=
callback
;
dbri
->
descs
[
last_td
].
output_callback_arg
=
callback_arg
;
for
(
td
=
first_td
;
td
!=
-
1
;
td
=
dbri
->
descs
[
td
].
next
)
{
dprintk
(
D_DESC
,
(
"DBRI TD %d: %08x %08x %08x %08x
\n
"
,
td
,
dbri
->
dma
->
desc
[
td
].
word1
,
dbri
->
dma
->
desc
[
td
].
ba
,
dbri
->
dma
->
desc
[
td
].
nda
,
dbri
->
dma
->
desc
[
td
].
word4
));
}
save_and_cli
(
flags
);
if
(
pipe_active
(
dbri
,
pipe
))
{
/* Pipe is already active - find last TD in use
* and link our first TD onto its end. Then issue
* a CDP command to let the DBRI know there's more data.
*/
last_td
=
dbri
->
pipes
[
pipe
].
desc
;
while
(
dbri
->
descs
[
last_td
].
next
!=
-
1
)
last_td
=
dbri
->
descs
[
last_td
].
next
;
dbri
->
descs
[
last_td
].
next
=
first_td
;
dbri
->
dma
->
desc
[
last_td
].
nda
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
first_td
);
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_CDP
,
0
,
pipe
);
dbri_cmdsend
(
dbri
,
cmd
);
}
else
{
/* Pipe isn't active - issue an SDP command to start
* our chain of TDs running.
*/
dbri
->
pipes
[
pipe
].
desc
=
first_td
;
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_SDP
,
0
,
dbri
->
pipes
[
pipe
].
sdp
|
D_SDP_P
|
D_SDP_EVERY
|
D_SDP_C
);
*
(
cmd
++
)
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
first_td
);
dbri_cmdsend
(
dbri
,
cmd
);
}
restore_flags
(
flags
);
}
static
void
recv_on_pipe
(
struct
dbri
*
dbri
,
int
pipe
,
void
*
buffer
,
unsigned
int
len
,
void
(
*
callback
)(
void
*
,
int
,
unsigned
int
),
void
*
callback_arg
)
{
volatile
s32
*
cmd
;
int
first_rd
=
-
1
;
int
last_rd
=
-
1
;
int
rd
;
__u32
bus_buffer
,
bus_buffer_base
;
if
(
pipe
<
0
||
pipe
>
15
)
{
printk
(
"DBRI: recv_on_pipe: Illegal pipe number
\n
"
);
return
;
}
if
(
dbri
->
pipes
[
pipe
].
sdp
==
0
)
{
printk
(
"DBRI: recv_on_pipe: Uninitialized pipe %d
\n
"
,
pipe
);
return
;
}
if
(
dbri
->
pipes
[
pipe
].
sdp
&
D_SDP_TO_SER
)
{
printk
(
"DBRI: recv_on_pipe: Called on transmit pipe %d
\n
"
,
pipe
);
return
;
}
/* XXX Fix this XXX
* Should be able to queue multiple buffers to receive on a pipe
*/
if
(
dbri
->
pipes
[
pipe
].
desc
!=
-
1
)
{
printk
(
"DBRI: recv_on_pipe: Called on active pipe %d
\n
"
,
pipe
);
return
;
}
/* Make sure buffer size is multiple of four */
len
&=
~
3
;
bus_buffer_base
=
bus_buffer
=
sbus_map_single
(
dbri
->
sdev
,
buffer
,
len
,
SBUS_DMA_FROMDEVICE
);
while
(
len
>
0
)
{
int
rd
,
mylen
;
if
(
len
>
((
1
<<
13
)
-
4
))
{
mylen
=
(
1
<<
13
)
-
4
;
}
else
{
mylen
=
len
;
}
for
(
rd
=
0
;
rd
<
DBRI_NO_DESCS
;
rd
++
)
{
if
(
!
dbri
->
descs
[
rd
].
inuse
)
break
;
}
if
(
rd
==
DBRI_NO_DESCS
)
{
printk
(
"DBRI recv_on_pipe: No descriptors
\n
"
);
break
;
}
dbri
->
dma
->
desc
[
rd
].
word1
=
0
;
dbri
->
dma
->
desc
[
rd
].
ba
=
bus_buffer
;
dbri
->
dma
->
desc
[
rd
].
nda
=
0
;
dbri
->
dma
->
desc
[
rd
].
word4
=
DBRI_RD_B
|
DBRI_RD_BCNT
(
mylen
);
dbri
->
descs
[
rd
].
buffer
=
NULL
;
dbri
->
descs
[
rd
].
len
=
0
;
dbri
->
descs
[
rd
].
input_callback
=
NULL
;
dbri
->
descs
[
rd
].
output_callback
=
NULL
;
dbri
->
descs
[
rd
].
next
=
-
1
;
dbri
->
descs
[
rd
].
inuse
=
1
;
if
(
first_rd
==
-
1
)
first_rd
=
rd
;
if
(
last_rd
!=
-
1
)
{
dbri
->
dma
->
desc
[
last_rd
].
nda
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
rd
);
dbri
->
descs
[
last_rd
].
next
=
rd
;
}
last_rd
=
rd
;
bus_buffer
+=
mylen
;
len
-=
mylen
;
}
if
(
last_rd
==
-
1
||
first_rd
==
-
1
)
{
sbus_unmap_single
(
dbri
->
sdev
,
bus_buffer_base
,
bus_buffer
-
bus_buffer_base
+
len
,
SBUS_DMA_FROMDEVICE
);
return
;
}
for
(
rd
=
first_rd
;
rd
!=
-
1
;
rd
=
dbri
->
descs
[
rd
].
next
)
{
dprintk
(
D_DESC
,
(
"DBRI RD %d: %08x %08x %08x %08x
\n
"
,
rd
,
dbri
->
dma
->
desc
[
rd
].
word1
,
dbri
->
dma
->
desc
[
rd
].
ba
,
dbri
->
dma
->
desc
[
rd
].
nda
,
dbri
->
dma
->
desc
[
rd
].
word4
));
}
dbri
->
descs
[
last_rd
].
buffer
=
buffer
;
dbri
->
descs
[
last_rd
].
buffer_dvma
=
bus_buffer_base
;
dbri
->
descs
[
last_rd
].
len
=
bus_buffer
-
bus_buffer_base
+
len
;
dbri
->
descs
[
last_rd
].
input_callback
=
callback
;
dbri
->
descs
[
last_rd
].
input_callback_arg
=
callback_arg
;
dbri
->
pipes
[
pipe
].
desc
=
first_rd
;
cmd
=
dbri_cmdlock
(
dbri
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_SDP
,
0
,
dbri
->
pipes
[
pipe
].
sdp
|
D_SDP_P
|
D_SDP_C
);
*
(
cmd
++
)
=
dbri
->
dma_dvma
+
dbri_dma_off
(
desc
,
first_rd
);
dbri_cmdsend
(
dbri
,
cmd
);
}
/*
****************************************************************************
************************** DBRI - CHI interface ****************************
****************************************************************************
The CHI is a four-wire (clock, frame sync, data in, data out) time-division
multiplexed serial interface which the DBRI can operate in either master
(give clock/frame sync) or slave (take clock/frame sync) mode.
*/
enum
master_or_slave
{
CHImaster
,
CHIslave
};
static
void
reset_chi
(
struct
dbri
*
dbri
,
enum
master_or_slave
master_or_slave
,
int
bits_per_frame
)
{
volatile
s32
*
cmd
;
int
val
;
static
int
chi_initialized
=
0
;
if
(
!
chi_initialized
)
{
cmd
=
dbri_cmdlock
(
dbri
);
/* Set CHI Anchor: Pipe 16 */
val
=
D_DTS_VI
|
D_DTS_INS
|
D_DTS_PRVIN
(
16
)
|
D_PIPE
(
16
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
D_TS_ANCHOR
|
D_TS_NEXT
(
16
);
*
(
cmd
++
)
=
0
;
val
=
D_DTS_VO
|
D_DTS_INS
|
D_DTS_PRVOUT
(
16
)
|
D_PIPE
(
16
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_DTS
,
0
,
val
);
*
(
cmd
++
)
=
0
;
*
(
cmd
++
)
=
D_TS_ANCHOR
|
D_TS_NEXT
(
16
);
dbri
->
pipes
[
16
].
sdp
=
1
;
dbri
->
pipes
[
16
].
nextpipe
=
16
;
dbri
->
chi_in_pipe
=
16
;
dbri
->
chi_out_pipe
=
16
;
#if 0
chi_initialized ++;
#endif
}
else
{
int
pipe
;
for
(
pipe
=
dbri
->
chi_in_pipe
;
pipe
!=
16
;
pipe
=
dbri
->
pipes
[
pipe
].
nextpipe
)
{
unlink_time_slot
(
dbri
,
pipe
,
PIPEinput
,
16
,
dbri
->
pipes
[
pipe
].
nextpipe
);
}
for
(
pipe
=
dbri
->
chi_out_pipe
;
pipe
!=
16
;
pipe
=
dbri
->
pipes
[
pipe
].
nextpipe
)
{
unlink_time_slot
(
dbri
,
pipe
,
PIPEoutput
,
16
,
dbri
->
pipes
[
pipe
].
nextpipe
);
}
dbri
->
chi_in_pipe
=
16
;
dbri
->
chi_out_pipe
=
16
;
cmd
=
dbri_cmdlock
(
dbri
);
}
if
(
master_or_slave
==
CHIslave
)
{
/* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
*
* CHICM = 0 (slave mode, 8 kHz frame rate)
* IR = give immediate CHI status interrupt
* EN = give CHI status interrupt upon change
*/
*
(
cmd
++
)
=
DBRI_CMD
(
D_CHI
,
0
,
D_CHI_CHICM
(
0
));
}
else
{
/* Setup DBRI for CHI Master - generate clock, FS
*
* BPF = bits per 8 kHz frame
* 12.288 MHz / CHICM_divisor = clock rate
* FD = 1 - drive CHIFS on rising edge of CHICK
*/
int
clockrate
=
bits_per_frame
*
8
;
int
divisor
=
12288
/
clockrate
;
if
(
divisor
>
255
||
divisor
*
clockrate
!=
12288
)
printk
(
"DBRI: illegal bits_per_frame in setup_chi
\n
"
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_CHI
,
0
,
D_CHI_CHICM
(
divisor
)
|
D_CHI_FD
|
D_CHI_BPF
(
bits_per_frame
));
}
dbri
->
chi_bpf
=
bits_per_frame
;
/* CHI Data Mode
*
* RCE = 0 - receive on falling edge of CHICK
* XCE = 1 - transmit on rising edge of CHICK
* XEN = 1 - enable transmitter
* REN = 1 - enable receiver
*/
*
(
cmd
++
)
=
DBRI_CMD
(
D_PAUSE
,
0
,
0
);
*
(
cmd
++
)
=
DBRI_CMD
(
D_CDM
,
0
,
D_CDM_XCE
|
D_CDM_XEN
|
D_CDM_REN
);
dbri_cmdsend
(
dbri
,
cmd
);
}
/*
****************************************************************************
*********************** CS4215 audio codec management **********************
****************************************************************************
In the standard SPARC audio configuration, the CS4215 codec is attached
to the DBRI via the CHI interface and few of the DBRI's PIO pins.
*/
static
void
mmcodec_default
(
struct
cs4215
*
mm
)
{
/*
* No action, memory resetting only.
*
* Data Time Slot 5-8
* Speaker,Line and Headphone enable. Gain set to the half.
* Input is mike.
*/
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
|
CS4215_PIO0
|
CS4215_PIO1
;
mm
->
data
[
3
]
=
CS4215_RG
(
0x8
)
|
CS4215_MA
(
0xf
);
/*
* Control Time Slot 1-4
* 0: Default I/O voltage scale
* 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
* 2: Serial enable, CHI master, 128 bits per frame, clock 1
* 3: Tests disabled
*/
mm
->
ctrl
[
0
]
=
CS4215_RSRVD_1
|
CS4215_MLB
;
mm
->
ctrl
[
1
]
=
CS4215_DFR_ULAW
|
CS4215_FREQ
[
0
].
csval
;
mm
->
ctrl
[
2
]
=
CS4215_XCLK
|
CS4215_BSEL_128
|
CS4215_FREQ
[
0
].
xtal
;
mm
->
ctrl
[
3
]
=
0
;
}
static
void
mmcodec_setup_pipes
(
struct
dbri
*
dbri
)
{
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
* Pipe 20: Send timeslots 5-8 (part of ctrl data)
* Pipe 6: Receive timeslots 1-4 (audio data)
* Pipe 21: 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).
*
* Control mode:
* Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
* Pipe 18: Receive timeslot 1 (clb).
* Pipe 19: Receive timeslot 7 (version).
*/
setup_pipe
(
dbri
,
4
,
D_SDP_MEM
|
D_SDP_TO_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
20
,
D_SDP_FIXED
|
D_SDP_TO_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
6
,
D_SDP_MEM
|
D_SDP_FROM_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
21
,
D_SDP_FIXED
|
D_SDP_FROM_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
17
,
D_SDP_FIXED
|
D_SDP_TO_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
18
,
D_SDP_FIXED
|
D_SDP_FROM_SER
|
D_SDP_MSB
);
setup_pipe
(
dbri
,
19
,
D_SDP_FIXED
|
D_SDP_FROM_SER
|
D_SDP_MSB
);
dbri
->
mm
.
status
=
0
;
recv_fixed
(
dbri
,
18
,
&
dbri
->
mm
.
status
);
recv_fixed
(
dbri
,
19
,
&
dbri
->
mm
.
version
);
}
static
void
mmcodec_setgain
(
struct
dbri
*
dbri
,
int
muted
)
{
if
(
muted
||
dbri
->
perchip_info
.
output_muted
)
{
dbri
->
mm
.
data
[
0
]
=
63
;
dbri
->
mm
.
data
[
1
]
=
63
;
}
else
{
int
left_gain
=
(
dbri
->
perchip_info
.
play
.
gain
/
4
)
%
64
;
int
right_gain
=
(
dbri
->
perchip_info
.
play
.
gain
/
4
)
%
64
;
int
outport
=
dbri
->
perchip_info
.
play
.
port
;
if
(
dbri
->
perchip_info
.
play
.
balance
<
AUDIO_MID_BALANCE
)
{
right_gain
*=
dbri
->
perchip_info
.
play
.
balance
;
right_gain
/=
AUDIO_MID_BALANCE
;
}
else
{
left_gain
*=
AUDIO_RIGHT_BALANCE
-
dbri
->
perchip_info
.
play
.
balance
;
left_gain
/=
AUDIO_MID_BALANCE
;
}
dprintk
(
D_MM
,
(
"DBRI: Setting codec gain left: %d right: %d
\n
"
,
left_gain
,
right_gain
));
dbri
->
mm
.
data
[
0
]
=
(
63
-
left_gain
);
if
(
outport
&
AUDIO_HEADPHONE
)
dbri
->
mm
.
data
[
0
]
|=
CS4215_HE
;
if
(
outport
&
AUDIO_LINE_OUT
)
dbri
->
mm
.
data
[
0
]
|=
CS4215_LE
;
dbri
->
mm
.
data
[
1
]
=
(
63
-
right_gain
);
if
(
outport
&
AUDIO_SPEAKER
)
dbri
->
mm
.
data
[
1
]
|=
CS4215_SE
;
}
xmit_fixed
(
dbri
,
20
,
*
(
int
*
)
dbri
->
mm
.
data
);
}
static
void
mmcodec_init_data
(
struct
dbri
*
dbri
)
{
int
data_width
;
u32
tmp
;
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
* Pipe 20: Send timeslots 5-8 (part of ctrl data)
* Pipe 6: Receive timeslots 1-4 (audio data)
* Pipe 21: 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...
*/
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
&=
~
(
D_C
);
/* Disable CHI */
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
/* Switch CS4215 to data mode - set PIO3 to 1 */
sbus_writel
(
D_ENPIO
|
D_PIO1
|
D_PIO3
|
(
dbri
->
mm
.
onboard
?
D_PIO0
:
D_PIO2
),
dbri
->
regs
+
REG2
);
reset_chi
(
dbri
,
CHIslave
,
128
);
/* Note: this next doesn't work for 8-bit stereo, because the two
* channels would be on timeslots 1 and 3, with 2 and 4 idle.
* (See CS4215 datasheet Fig 15)
*
* DBRI non-contiguous mode would be required to make this work.
*/
data_width
=
dbri
->
perchip_info
.
play
.
channels
*
dbri
->
perchip_info
.
play
.
precision
;
link_time_slot
(
dbri
,
20
,
PIPEoutput
,
16
,
32
,
dbri
->
mm
.
offset
+
32
);
link_time_slot
(
dbri
,
4
,
PIPEoutput
,
16
,
data_width
,
dbri
->
mm
.
offset
);
link_time_slot
(
dbri
,
6
,
PIPEinput
,
16
,
data_width
,
dbri
->
mm
.
offset
);
link_time_slot
(
dbri
,
21
,
PIPEinput
,
16
,
16
,
dbri
->
mm
.
offset
+
40
);
mmcodec_setgain
(
dbri
,
0
);
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
|=
D_C
;
/* Enable CHI */
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
}
/*
* Send the control information (i.e. audio format)
*/
static
int
mmcodec_setctrl
(
struct
dbri
*
dbri
)
{
int
i
,
val
;
u32
tmp
;
/* XXX - let the CPU do something useful during these delays */
/* Temporarily mute outputs, and wait 1/8000 sec (125 us)
* to make sure this takes. This avoids clicking noises.
*/
mmcodec_setgain
(
dbri
,
1
);
udelay
(
125
);
/*
* 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
);
sbus_writel
(
val
,
dbri
->
regs
+
REG2
);
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, on a SPARCStation 20 (and maybe
* others?), 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?
*/
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
&=
~
D_C
;
/* Disable CHI */
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
reset_chi
(
dbri
,
CHImaster
,
128
);
/*
* Control mode:
* Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
* Pipe 18: Receive timeslot 1 (clb).
* Pipe 19: Receive timeslot 7 (version).
*/
link_time_slot
(
dbri
,
17
,
PIPEoutput
,
16
,
32
,
dbri
->
mm
.
offset
);
link_time_slot
(
dbri
,
18
,
PIPEinput
,
16
,
8
,
dbri
->
mm
.
offset
);
link_time_slot
(
dbri
,
19
,
PIPEinput
,
16
,
8
,
dbri
->
mm
.
offset
+
48
);
/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
dbri
->
mm
.
ctrl
[
0
]
&=
~
CS4215_CLB
;
xmit_fixed
(
dbri
,
17
,
*
(
int
*
)
dbri
->
mm
.
ctrl
);
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
|=
D_C
;
/* Enable CHI */
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
i
=
64
;
while
(((
dbri
->
mm
.
status
&
0xe4
)
!=
0x20
)
&&
--
i
)
udelay
(
125
);
if
(
i
==
0
)
{
dprintk
(
D_MM
,
(
"DBRI: CS4215 didn't respond to CLB (0x%02x)
\n
"
,
dbri
->
mm
.
status
));
return
-
1
;
}
/* Terminate CS4215 control mode - data sheet says
* "Set CLB=1 and send two more frames of valid control info"
*/
dbri
->
mm
.
ctrl
[
0
]
|=
CS4215_CLB
;
xmit_fixed
(
dbri
,
17
,
*
(
int
*
)
dbri
->
mm
.
ctrl
);
/* Two frames of control info @ 8kHz frame rate = 250 us delay */
udelay
(
250
);
mmcodec_setgain
(
dbri
,
0
);
return
0
;
}
static
int
mmcodec_init
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
u32
reg2
=
sbus_readl
(
dbri
->
regs
+
REG2
);
/* Look for the cs4215 chips */
if
(
reg2
&
D_PIO2
)
{
dprintk
(
D_MM
,
(
"DBRI: Onboard CS4215 detected
\n
"
));
dbri
->
mm
.
onboard
=
1
;
}
if
(
reg2
&
D_PIO0
)
{
dprintk
(
D_MM
,
(
"DBRI: Speakerbox detected
\n
"
));
dbri
->
mm
.
onboard
=
0
;
}
/* Using the Speakerbox, if both are attached. */
if
((
reg2
&
D_PIO2
)
&&
(
reg2
&
D_PIO0
))
{
printk
(
"DBRI: Using speakerbox / ignoring onboard mmcodec.
\n
"
);
sbus_writel
(
D_ENPIO2
,
dbri
->
regs
+
REG2
);
dbri
->
mm
.
onboard
=
0
;
}
if
(
!
(
reg2
&
(
D_PIO0
|
D_PIO2
)))
{
printk
(
"DBRI: no mmcodec found.
\n
"
);
return
-
EIO
;
}
mmcodec_setup_pipes
(
dbri
);
mmcodec_default
(
&
dbri
->
mm
);
dbri
->
mm
.
version
=
0xff
;
dbri
->
mm
.
offset
=
dbri
->
mm
.
onboard
?
0
:
8
;
if
(
mmcodec_setctrl
(
dbri
)
==
-
1
||
dbri
->
mm
.
version
==
0xff
)
{
dprintk
(
D_MM
,
(
"DBRI: CS4215 failed probe at offset %d
\n
"
,
dbri
->
mm
.
offset
));
return
-
EIO
;
}
dprintk
(
D_MM
,
(
"DBRI: Found CS4215 at offset %d
\n
"
,
dbri
->
mm
.
offset
));
dbri
->
perchip_info
.
play
.
channels
=
1
;
dbri
->
perchip_info
.
play
.
precision
=
8
;
dbri
->
perchip_info
.
play
.
gain
=
(
AUDIO_MAX_GAIN
*
7
/
10
);
/* 70% */
dbri
->
perchip_info
.
play
.
balance
=
AUDIO_MID_BALANCE
;
dbri
->
perchip_info
.
play
.
port
=
dbri
->
perchip_info
.
play
.
avail_ports
=
AUDIO_SPEAKER
|
AUDIO_HEADPHONE
|
AUDIO_LINE_OUT
;
dbri
->
perchip_info
.
record
.
port
=
AUDIO_MICROPHONE
;
dbri
->
perchip_info
.
record
.
avail_ports
=
AUDIO_MICROPHONE
|
AUDIO_LINE_IN
;
mmcodec_init_data
(
dbri
);
return
0
;
}
/*
****************************************************************************
******************** Interface with sparcaudio midlevel ********************
****************************************************************************
The sparcaudio midlevel is contained in the file audio.c. It interfaces
to the user process and performs buffering, intercepts SunOS-style ioctl's,
etc. It interfaces to a abstract audio device via a struct sparcaudio_driver.
This code presents such an interface for the DBRI with an attached CS4215.
All our routines are defined, and then comes our struct sparcaudio_driver.
*/
/******************* sparcaudio midlevel - audio output *******************/
static
void
dbri_audio_output_callback
(
void
*
callback_arg
,
int
status
)
{
struct
sparcaudio_driver
*
drv
=
callback_arg
;
if
(
status
!=
-
1
)
sparcaudio_output_done
(
drv
,
1
);
}
static
void
dbri_start_output
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
dprintk
(
D_USR
,
(
"DBRI: start audio output buf=%p/%ld
\n
"
,
buffer
,
count
));
/* Pipe 4 is audio transmit */
xmit_on_pipe
(
dbri
,
4
,
buffer
,
count
,
&
dbri_audio_output_callback
,
drv
);
#if 0
/* Notify midlevel that we're a DMA-capable driver that
* can accept another buffer immediately. We should probably
* check that we've got enough resources (i.e, descriptors)
* available before doing this, but the default midlevel
* settings only buffer 64KB, which we can handle with 16
* of our DBRI_NO_DESCS (64) descriptors.
*
* This code is #ifdef'ed out because it's caused me more
* problems than it solved. It'd be nice to provide the
* DBRI with a chain of buffers, but the midlevel code is
* so tricky that I really don't want to deal with it.
*/
sparcaudio_output_done(drv, 2);
#endif
}
static
void
dbri_stop_output
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
reset_pipe
(
dbri
,
4
);
}
/******************* sparcaudio midlevel - audio input ********************/
static
void
dbri_audio_input_callback
(
void
*
callback_arg
,
int
status
,
unsigned
int
len
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
callback_arg
;
if
(
status
!=
-
1
)
sparcaudio_input_done
(
drv
,
3
);
}
static
void
dbri_start_input
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
len
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
/* Pipe 6 is audio receive */
recv_on_pipe
(
dbri
,
6
,
buffer
,
len
,
&
dbri_audio_input_callback
,
(
void
*
)
drv
);
dprintk
(
D_USR
,
(
"DBRI: start audio input buf=%p/%ld
\n
"
,
buffer
,
len
));
}
static
void
dbri_stop_input
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
reset_pipe
(
dbri
,
6
);
}
/******************* sparcaudio midlevel - volume & balance ***************/
static
int
dbri_set_output_volume
(
struct
sparcaudio_driver
*
drv
,
int
volume
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
dbri
->
perchip_info
.
play
.
gain
=
volume
;
mmcodec_setgain
(
dbri
,
0
);
return
0
;
}
static
int
dbri_get_output_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
gain
;
}
static
int
dbri_set_input_volume
(
struct
sparcaudio_driver
*
drv
,
int
volume
)
{
return
0
;
}
static
int
dbri_get_input_volume
(
struct
sparcaudio_driver
*
drv
)
{
return
0
;
}
static
int
dbri_set_monitor_volume
(
struct
sparcaudio_driver
*
drv
,
int
volume
)
{
return
0
;
}
static
int
dbri_get_monitor_volume
(
struct
sparcaudio_driver
*
drv
)
{
return
0
;
}
static
int
dbri_set_output_balance
(
struct
sparcaudio_driver
*
drv
,
int
balance
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
dbri
->
perchip_info
.
play
.
balance
=
balance
;
mmcodec_setgain
(
dbri
,
0
);
return
0
;
}
static
int
dbri_get_output_balance
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
balance
;
}
static
int
dbri_set_input_balance
(
struct
sparcaudio_driver
*
drv
,
int
balance
)
{
return
0
;
}
static
int
dbri_get_input_balance
(
struct
sparcaudio_driver
*
drv
)
{
return
0
;
}
static
int
dbri_set_output_muted
(
struct
sparcaudio_driver
*
drv
,
int
mute
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
dbri
->
perchip_info
.
output_muted
=
mute
;
return
0
;
}
static
int
dbri_get_output_muted
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
output_muted
;
}
/******************* sparcaudio midlevel - encoding format ****************/
static
int
dbri_set_output_channels
(
struct
sparcaudio_driver
*
drv
,
int
chan
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
switch
(
chan
)
{
case
0
:
return
0
;
case
1
:
dbri
->
mm
.
ctrl
[
1
]
&=
~
CS4215_DFR_STEREO
;
break
;
case
2
:
dbri
->
mm
.
ctrl
[
1
]
|=
CS4215_DFR_STEREO
;
break
;
default:
return
-
1
;
}
dbri
->
perchip_info
.
play
.
channels
=
chan
;
mmcodec_setctrl
(
dbri
);
mmcodec_init_data
(
dbri
);
return
0
;
}
static
int
dbri_get_output_channels
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
channels
;
}
static
int
dbri_set_input_channels
(
struct
sparcaudio_driver
*
drv
,
int
chan
)
{
return
dbri_set_output_channels
(
drv
,
chan
);
}
static
int
dbri_get_input_channels
(
struct
sparcaudio_driver
*
drv
)
{
return
dbri_get_output_channels
(
drv
);
}
static
int
dbri_set_output_precision
(
struct
sparcaudio_driver
*
drv
,
int
prec
)
{
return
0
;
}
static
int
dbri_get_output_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
precision
;
}
static
int
dbri_set_input_precision
(
struct
sparcaudio_driver
*
drv
,
int
prec
)
{
return
0
;
}
static
int
dbri_get_input_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
precision
;
}
static
int
dbri_set_output_encoding
(
struct
sparcaudio_driver
*
drv
,
int
enc
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
/* For ULAW and ALAW, audio.c enforces precision = 8,
* for LINEAR, precision must be 16
*/
switch
(
enc
)
{
case
AUDIO_ENCODING_NONE
:
return
0
;
case
AUDIO_ENCODING_ULAW
:
dbri
->
mm
.
ctrl
[
1
]
&=
~
3
;
dbri
->
mm
.
ctrl
[
1
]
|=
CS4215_DFR_ULAW
;
dbri
->
perchip_info
.
play
.
encoding
=
enc
;
dbri
->
perchip_info
.
play
.
precision
=
8
;
break
;
case
AUDIO_ENCODING_ALAW
:
dbri
->
mm
.
ctrl
[
1
]
&=
~
3
;
dbri
->
mm
.
ctrl
[
1
]
|=
CS4215_DFR_ALAW
;
dbri
->
perchip_info
.
play
.
encoding
=
enc
;
dbri
->
perchip_info
.
play
.
precision
=
8
;
break
;
case
AUDIO_ENCODING_LINEAR
:
dbri
->
mm
.
ctrl
[
1
]
&=
~
3
;
dbri
->
mm
.
ctrl
[
1
]
|=
CS4215_DFR_LINEAR16
;
dbri
->
perchip_info
.
play
.
encoding
=
enc
;
dbri
->
perchip_info
.
play
.
precision
=
16
;
break
;
default:
return
-
1
;
};
mmcodec_setctrl
(
dbri
);
mmcodec_init_data
(
dbri
);
return
0
;
}
static
int
dbri_get_output_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
encoding
;
}
static
int
dbri_set_input_encoding
(
struct
sparcaudio_driver
*
drv
,
int
enc
)
{
return
dbri_set_output_encoding
(
drv
,
enc
);
}
static
int
dbri_get_input_encoding
(
struct
sparcaudio_driver
*
drv
)
{
return
dbri_get_output_encoding
(
drv
);
}
static
int
dbri_set_output_rate
(
struct
sparcaudio_driver
*
drv
,
int
rate
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
int
i
;
if
(
rate
==
0
)
return
0
;
for
(
i
=
0
;
CS4215_FREQ
[
i
].
freq
;
i
++
)
{
if
(
CS4215_FREQ
[
i
].
freq
==
rate
)
break
;
}
if
(
CS4215_FREQ
[
i
].
freq
==
0
)
return
-
1
;
dbri
->
mm
.
ctrl
[
1
]
&=
~
0x38
;
dbri
->
mm
.
ctrl
[
1
]
|=
CS4215_FREQ
[
i
].
csval
;
dbri
->
mm
.
ctrl
[
2
]
&=
~
0x70
;
dbri
->
mm
.
ctrl
[
2
]
|=
CS4215_FREQ
[
i
].
xtal
;
dbri
->
perchip_info
.
play
.
sample_rate
=
rate
;
mmcodec_setctrl
(
dbri
);
mmcodec_init_data
(
dbri
);
return
0
;
}
static
int
dbri_get_output_rate
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
sample_rate
;
}
static
int
dbri_set_input_rate
(
struct
sparcaudio_driver
*
drv
,
int
rate
)
{
return
dbri_set_output_rate
(
drv
,
rate
);
}
static
int
dbri_get_input_rate
(
struct
sparcaudio_driver
*
drv
)
{
return
dbri_get_output_rate
(
drv
);
}
/******************* sparcaudio midlevel - ports ***********************/
static
int
dbri_set_output_port
(
struct
sparcaudio_driver
*
drv
,
int
port
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
port
&=
dbri
->
perchip_info
.
play
.
avail_ports
;
dbri
->
perchip_info
.
play
.
port
=
port
;
mmcodec_setgain
(
dbri
,
0
);
return
0
;
}
static
int
dbri_get_output_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
port
;
}
static
int
dbri_set_input_port
(
struct
sparcaudio_driver
*
drv
,
int
port
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
port
&=
dbri
->
perchip_info
.
record
.
avail_ports
;
dbri
->
perchip_info
.
record
.
port
=
port
;
mmcodec_setgain
(
dbri
,
0
);
return
0
;
}
static
int
dbri_get_input_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
record
.
port
;
}
static
int
dbri_get_output_ports
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
play
.
avail_ports
;
}
static
int
dbri_get_input_ports
(
struct
sparcaudio_driver
*
drv
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
return
dbri
->
perchip_info
.
record
.
avail_ports
;
}
/******************* sparcaudio midlevel - driver ID ********************/
static
void
dbri_audio_getdev
(
struct
sparcaudio_driver
*
drv
,
audio_device_t
*
audinfo
)
{
struct
dbri
*
dbri
=
(
struct
dbri
*
)
drv
->
private
;
strncpy
(
audinfo
->
name
,
"SUNW,DBRI"
,
sizeof
(
audinfo
->
name
)
-
1
);
audinfo
->
version
[
0
]
=
dbri
->
dbri_version
;
audinfo
->
version
[
1
]
=
'\0'
;
strncpy
(
audinfo
->
config
,
"onboard1"
,
sizeof
(
audinfo
->
config
)
-
1
);
}
static
int
dbri_sunaudio_getdev_sunos
(
struct
sparcaudio_driver
*
drv
)
{
return
AUDIO_DEV_CODEC
;
}
/******************* sparcaudio midlevel - open & close ******************/
static
int
dbri_open
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
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
int
dbri_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
x
,
unsigned
long
y
,
struct
sparcaudio_driver
*
drv
)
{
return
-
EINVAL
;
}
/*********** sparcaudio midlevel - struct sparcaudio_driver ************/
static
struct
sparcaudio_operations
dbri_ops
=
{
dbri_open
,
dbri_release
,
dbri_ioctl
,
dbri_start_output
,
dbri_stop_output
,
dbri_start_input
,
dbri_stop_input
,
dbri_audio_getdev
,
dbri_set_output_volume
,
dbri_get_output_volume
,
dbri_set_input_volume
,
dbri_get_input_volume
,
dbri_set_monitor_volume
,
dbri_get_monitor_volume
,
dbri_set_output_balance
,
dbri_get_output_balance
,
dbri_set_input_balance
,
dbri_get_input_balance
,
dbri_set_output_channels
,
dbri_get_output_channels
,
dbri_set_input_channels
,
dbri_get_input_channels
,
dbri_set_output_precision
,
dbri_get_output_precision
,
dbri_set_input_precision
,
dbri_get_input_precision
,
dbri_set_output_port
,
dbri_get_output_port
,
dbri_set_input_port
,
dbri_get_input_port
,
dbri_set_output_encoding
,
dbri_get_output_encoding
,
dbri_set_input_encoding
,
dbri_get_input_encoding
,
dbri_set_output_rate
,
dbri_get_output_rate
,
dbri_set_input_rate
,
dbri_get_input_rate
,
dbri_sunaudio_getdev_sunos
,
dbri_get_output_ports
,
dbri_get_input_ports
,
dbri_set_output_muted
,
dbri_get_output_muted
,
};
/*
****************************************************************************
************************** ISDN (Hisax) Interface **************************
****************************************************************************
*/
void
dbri_isdn_init
(
struct
dbri
*
dbri
)
{
/* 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
*/
setup_pipe
(
dbri
,
0
,
D_SDP_HDLC
|
D_SDP_FROM_SER
|
D_SDP_LSB
);
setup_pipe
(
dbri
,
8
,
D_SDP_HDLC
|
D_SDP_FROM_SER
|
D_SDP_LSB
);
setup_pipe
(
dbri
,
9
,
D_SDP_HDLC
|
D_SDP_FROM_SER
|
D_SDP_LSB
);
setup_pipe
(
dbri
,
1
,
D_SDP_HDLC_D
|
D_SDP_TO_SER
|
D_SDP_LSB
);
setup_pipe
(
dbri
,
10
,
D_SDP_HDLC
|
D_SDP_TO_SER
|
D_SDP_LSB
);
setup_pipe
(
dbri
,
11
,
D_SDP_HDLC
|
D_SDP_TO_SER
|
D_SDP_LSB
);
link_time_slot
(
dbri
,
0
,
PIPEinput
,
0
,
2
,
17
);
link_time_slot
(
dbri
,
8
,
PIPEinput
,
0
,
8
,
0
);
link_time_slot
(
dbri
,
9
,
PIPEinput
,
8
,
8
,
8
);
link_time_slot
(
dbri
,
1
,
PIPEoutput
,
1
,
2
,
17
);
link_time_slot
(
dbri
,
10
,
PIPEoutput
,
1
,
8
,
0
);
link_time_slot
(
dbri
,
11
,
PIPEoutput
,
10
,
8
,
8
);
}
int
dbri_get_irqnum
(
int
dev
)
{
struct
dbri
*
dbri
;
if
(
dev
>=
num_drivers
)
return
(
0
);
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
tprintk
((
"dbri_get_irqnum()
\n
"
));
/* 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
;
tprintk
((
"dbri_get_liu_state() returns %d
\n
"
,
dbri
->
liu_state
));
return
dbri
->
liu_state
;
}
void
dbri_liu_activate
(
int
dev
,
int
priority
);
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
;
tprintk
((
"dbri_liu_init()
\n
"
));
/* Set callback for LIU state change */
dbri
->
liu_callback
=
callback
;
dbri
->
liu_callback_arg
=
callback_arg
;
dbri_isdn_init
(
dbri
);
dbri_liu_activate
(
dev
,
0
);
}
void
dbri_liu_activate
(
int
dev
,
int
priority
)
{
struct
dbri
*
dbri
;
int
val
;
volatile
s32
*
cmd
;
if
(
dev
>=
num_drivers
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
tprintk
((
"dbri_liu_activate()
\n
"
));
if
(
dbri
->
liu_state
<=
3
)
{
u32
tmp
;
cmd
=
dbri_cmdlock
(
dbri
);
/* Turn on the ISDN TE interface and request activation */
val
=
D_NT_IRM_IMM
|
D_NT_IRM_EN
|
D_NT_ACT
;
#ifdef LOOPBACK_D
val
|=
D_NT_LLB
(
4
);
#endif
*
(
cmd
++
)
=
DBRI_CMD
(
D_TE
,
0
,
val
);
dbri_cmdsend
(
dbri
,
cmd
);
/* Activate the interface */
tmp
=
sbus_readl
(
dbri
->
regs
+
REG0
);
tmp
|=
D_T
;
sbus_writel
(
tmp
,
dbri
->
regs
+
REG0
);
}
}
void
dbri_liu_deactivate
(
int
dev
)
{
struct
dbri
*
dbri
;
#if 0
u32 tmp;
#endif
if
(
dev
>=
num_drivers
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
tprintk
((
"dbri_liu_deactivate()
\n
"
));
#if 0
/* Turn off the ISDN TE interface */
tmp = sbus_readl(dbri->regs + REG0);
tmp &= ~D_T;
sbus_writel(tmp, dbri->regs + REG0);
dbri->liu_state = 0;
#endif
}
void
dbri_dxmit
(
int
dev
,
__u8
*
buffer
,
unsigned
int
count
,
void
(
*
callback
)(
void
*
,
int
),
void
*
callback_arg
)
{
struct
dbri
*
dbri
;
if
(
dev
>=
num_drivers
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
/* Pipe 1 is D channel transmit */
xmit_on_pipe
(
dbri
,
1
,
buffer
,
count
,
callback
,
callback_arg
);
}
void
dbri_drecv
(
int
dev
,
__u8
*
buffer
,
unsigned
int
size
,
void
(
*
callback
)(
void
*
,
int
,
unsigned
int
),
void
*
callback_arg
)
{
struct
dbri
*
dbri
;
if
(
dev
>=
num_drivers
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
/* Pipe 0 is D channel receive */
recv_on_pipe
(
dbri
,
0
,
buffer
,
size
,
callback
,
callback_arg
);
}
int
dbri_bopen
(
int
dev
,
unsigned
int
chan
,
int
hdlcmode
,
u_char
xmit_idle_char
)
{
struct
dbri
*
dbri
;
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 */
setup_pipe
(
dbri
,
8
+
chan
,
D_SDP_HDLC
|
D_SDP_FROM_SER
|
D_SDP_LSB
);
/* Pipe 10/11: transmit B1/B2 channel */
setup_pipe
(
dbri
,
10
+
chan
,
D_SDP_HDLC
|
D_SDP_TO_SER
|
D_SDP_LSB
);
}
else
{
/* !hdlcmode means transparent */
/* Pipe 8/9: receive B1/B2 channel */
setup_pipe
(
dbri
,
8
+
chan
,
D_SDP_MEM
|
D_SDP_FROM_SER
|
D_SDP_LSB
);
/* Pipe 10/11: transmit B1/B2 channel */
setup_pipe
(
dbri
,
10
+
chan
,
D_SDP_MEM
|
D_SDP_TO_SER
|
D_SDP_LSB
);
}
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
;
reset_pipe
(
dbri
,
8
+
chan
);
reset_pipe
(
dbri
,
10
+
chan
);
}
void
dbri_bxmit
(
int
dev
,
unsigned
int
chan
,
__u8
*
buffer
,
unsigned
long
count
,
void
(
*
callback
)(
void
*
,
int
),
void
*
callback_arg
)
{
struct
dbri
*
dbri
;
if
(
dev
>=
num_drivers
||
chan
>
1
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
/* Pipe 10/11 is B1/B2 channel transmit */
xmit_on_pipe
(
dbri
,
10
+
chan
,
buffer
,
count
,
callback
,
callback_arg
);
}
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
;
if
(
dev
>=
num_drivers
||
chan
>
1
)
return
;
dbri
=
(
struct
dbri
*
)
drivers
[
dev
].
private
;
/* Pipe 8/9 is B1/B2 channel receive */
recv_on_pipe
(
dbri
,
8
+
chan
,
buffer
,
size
,
callback
,
callback_arg
);
}
#if defined(DBRI_ISDN)
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
sbus_dev
*
sdev
)
{
struct
dbri
*
dbri
;
struct
linux_prom_irqs
irq
;
int
err
;
if
(
sdev
->
prom_name
[
9
]
<
'e'
)
{
printk
(
KERN_ERR
"DBRI: unsupported chip version %c found.
\n
"
,
sdev
->
prom_name
[
9
]);
return
-
EIO
;
}
drv
->
ops
=
&
dbri_ops
;
drv
->
private
=
kmalloc
(
sizeof
(
struct
dbri
),
GFP_KERNEL
);
if
(
drv
->
private
==
NULL
)
return
-
ENOMEM
;
dbri
=
(
struct
dbri
*
)
drv
->
private
;
memset
(
dbri
,
0
,
sizeof
(
*
dbri
));
dbri
->
dma
=
sbus_alloc_consistent
(
sdev
,
sizeof
(
struct
dbri_dma
),
&
dbri
->
dma_dvma
);
memset
((
void
*
)
dbri
->
dma
,
0
,
sizeof
(
struct
dbri_dma
));
dprintk
(
D_GEN
,
(
"DBRI: DMA Cmd Block 0x%p (0x%08x)
\n
"
,
dbri
->
dma
,
dbri
->
dma_dvma
));
dbri
->
dbri_version
=
sdev
->
prom_name
[
9
];
dbri
->
sdev
=
sdev
;
/* Map the registers into memory. */
dbri
->
regs_size
=
sdev
->
reg_addrs
[
0
].
reg_size
;
dbri
->
regs
=
sbus_ioremap
(
&
sdev
->
resource
[
0
],
0
,
sdev
->
reg_addrs
[
0
].
reg_size
,
"DBRI Registers"
);
if
(
!
dbri
->
regs
)
{
printk
(
KERN_ERR
"DBRI: could not allocate registers
\n
"
);
sbus_free_consistent
(
sdev
,
sizeof
(
struct
dbri_dma
),
(
void
*
)
dbri
->
dma
,
dbri
->
dma_dvma
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
prom_getproperty
(
sdev
->
prom_node
,
"intr"
,
(
char
*
)
&
irq
,
sizeof
(
irq
));
dbri
->
irq
=
irq
.
pri
;
err
=
request_irq
(
dbri
->
irq
,
dbri_intr
,
SA_SHIRQ
,
"DBRI audio/ISDN"
,
dbri
);
if
(
err
)
{
printk
(
KERN_ERR
"DBRI: Can't get irq %d
\n
"
,
dbri
->
irq
);
sbus_iounmap
(
dbri
->
regs
,
dbri
->
regs_size
);
sbus_free_consistent
(
sdev
,
sizeof
(
struct
dbri_dma
),
(
void
*
)
dbri
->
dma
,
dbri
->
dma_dvma
);
kfree
(
drv
->
private
);
return
err
;
}
dbri_initialize
(
dbri
);
err
=
mmcodec_init
(
drv
);
if
(
err
)
{
dbri_detach
(
dbri
);
return
err
;
}
/* Register ourselves with the midlevel audio driver. */
err
=
register_sparcaudio_driver
(
drv
,
1
);
if
(
err
)
{
printk
(
KERN_ERR
"DBRI: unable to register audio
\n
"
);
dbri_detach
(
dbri
);
return
err
;
}
dbri
->
perchip_info
.
play
.
active
=
dbri
->
perchip_info
.
play
.
pause
=
0
;
dbri
->
perchip_info
.
record
.
active
=
dbri
->
perchip_info
.
record
.
pause
=
0
;
printk
(
KERN_INFO
"audio%d at 0x%lx (irq %d) is DBRI(%c)+CS4215(%d)
\n
"
,
num_drivers
,
dbri
->
regs
,
dbri
->
irq
,
dbri
->
dbri_version
,
dbri
->
mm
.
version
);
return
0
;
}
/* Probe for the dbri chip and then attach the driver. */
static
int
__init
dbri_init
(
void
)
{
struct
sbus_bus
*
sbus
;
struct
sbus_dev
*
sdev
;
num_drivers
=
0
;
/* Probe each SBUS for the DBRI chip(s). */
for_all_sbusdev
(
sdev
,
sbus
)
{
/*
* The version is coded in the last character
*/
if
(
!
strncmp
(
sdev
->
prom_name
,
"SUNW,DBRI"
,
9
))
{
dprintk
(
D_GEN
,
(
"DBRI: Found %s in SBUS slot %d
\n
"
,
sdev
->
prom_name
,
sdev
->
slot
));
if
(
num_drivers
>=
MAX_DRIVERS
)
{
printk
(
"DBRI: Ignoring slot %d
\n
"
,
sdev
->
slot
);
continue
;
}
if
(
dbri_attach
(
&
drivers
[
num_drivers
],
sdev
)
==
0
)
num_drivers
++
;
}
}
return
(
num_drivers
>
0
)
?
0
:
-
EIO
;
}
static
void
__exit
dbri_exit
(
void
)
{
register
int
i
;
for
(
i
=
0
;
i
<
num_drivers
;
i
++
)
{
dbri_detach
((
struct
dbri
*
)
drivers
[
i
].
private
);
unregister_sparcaudio_driver
(
&
drivers
[
i
],
1
);
num_drivers
--
;
}
}
module_init
(
dbri_init
);
module_exit
(
dbri_exit
);
MODULE_LICENSE
(
"GPL"
);
/*
* 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:
*/
drivers/sbus/audio/dbri.h
deleted
100644 → 0
View file @
2ee094ef
/* $Id: dbri.h,v 1.13 2000/10/13 00:34:24 uzi Exp $
* drivers/sbus/audio/cs4231.h
*
* Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
*/
#ifndef _DBRI_H_
#define _DBRI_H_
#include <linux/types.h>
/* DBRI main registers */
#define REG0 0x00UL
/* Status and Control */
#define REG1 0x04UL
/* Mode and Interrupt */
#define REG2 0x08UL
/* Parallel IO */
#define REG3 0x0cUL
/* Test */
#define REG8 0x20UL
/* Command Queue Pointer */
#define REG9 0x24UL
/* Interrupt Queue Pointer */
#define DBRI_NO_CMDS 64
#define DBRI_NO_INTS 1
/* Note: the value of this define was
* originally 2. The ringbuffer to store
* interrupts in dma is currently broken.
* This is a temporary fix until the ringbuffer
* is fixed.
*/
#define DBRI_INT_BLK 64
#define DBRI_NO_DESCS 64
#define DBRI_MM_ONB 1
#define DBRI_MM_SB 2
struct
dbri_mem
{
volatile
__u32
word1
;
volatile
__u32
ba
;
/* Transmit/Receive Buffer Address */
volatile
__u32
nda
;
/* Next Descriptor Address */
volatile
__u32
word4
;
};
#include "cs4215.h"
/* This structure is in a DMA region where it can accessed by both
* the CPU and the DBRI
*/
struct
dbri_dma
{
volatile
s32
cmd
[
DBRI_NO_CMDS
];
/* Place for commands */
volatile
s32
intr
[
DBRI_NO_INTS
*
DBRI_INT_BLK
];
/* Interrupt field */
struct
dbri_mem
desc
[
DBRI_NO_DESCS
];
/* Xmit/receive descriptors */
};
#define dbri_dma_off(member, elem) \
((u32)(unsigned long) \
(&(((struct dbri_dma *)0)->member[elem])))
enum
in_or_out
{
PIPEinput
,
PIPEoutput
};
enum
direction
{
in
,
out
};
struct
dbri_pipe
{
u32
sdp
;
/* SDP command word */
enum
direction
direction
;
int
nextpipe
;
/* Next pipe in linked list */
int
prevpipe
;
int
cycle
;
/* Offset of timeslot (bits) */
int
length
;
/* Length of timeslot (bits) */
int
desc
;
/* Index of active descriptor*/
volatile
__u32
*
recv_fixed_ptr
;
/* Ptr to receive fixed data */
};
struct
dbri_desc
{
int
inuse
;
/* Boolean flag */
int
next
;
/* Index of next desc, or -1 */
void
*
buffer
;
/* CPU view of buffer */
u32
buffer_dvma
;
/* Device view */
unsigned
int
len
;
void
(
*
output_callback
)(
void
*
,
int
);
void
*
output_callback_arg
;
void
(
*
input_callback
)(
void
*
,
int
,
unsigned
int
);
void
*
input_callback_arg
;
};
/* This structure holds the information for both chips (DBRI & CS4215) */
struct
dbri
{
int
regs_size
,
irq
;
/* Needed for unload */
struct
sbus_dev
*
sdev
;
/* SBUS device info */
volatile
struct
dbri_dma
*
dma
;
/* Pointer to our DMA block */
u32
dma_dvma
;
/* DBRI visible DMA address */
unsigned
long
regs
;
/* dbri HW regs */
int
dbri_version
;
/* 'e' and up is OK */
int
dbri_irqp
;
/* intr queue pointer */
int
wait_seen
;
struct
dbri_pipe
pipes
[
32
];
/* DBRI's 32 data pipes */
struct
dbri_desc
descs
[
DBRI_NO_DESCS
];
int
chi_in_pipe
;
int
chi_out_pipe
;
int
chi_bpf
;
struct
cs4215
mm
;
/* mmcodec special info */
#if 0
/* Where to sleep if busy */
wait_queue_head_t wait, int_wait;
#endif
struct
audio_info
perchip_info
;
/* Track ISDN LIU and notify changes */
int
liu_state
;
void
(
*
liu_callback
)(
void
*
);
void
*
liu_callback_arg
;
};
/* DBRI Reg0 - Status Control Register - defines. (Page 17) */
#define D_P (1<<15)
/* Program command & queue pointer valid */
#define D_G (1<<14)
/* Allow 4-Word SBus Burst */
#define D_S (1<<13)
/* Allow 16-Word SBus Burst */
#define D_E (1<<12)
/* Allow 8-Word SBus Burst */
#define D_X (1<<7)
/* Sanity Timer Disable */
#define D_T (1<<6)
/* Permit activation of the TE interface */
#define D_N (1<<5)
/* Permit activation of the NT interface */
#define D_C (1<<4)
/* Permit activation of the CHI interface */
#define D_F (1<<3)
/* Force Sanity Timer Time-Out */
#define D_D (1<<2)
/* Disable Master Mode */
#define D_H (1<<1)
/* Halt for Analysis */
#define D_R (1<<0)
/* Soft Reset */
/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */
#define D_LITTLE_END (1<<8)
/* Byte Order */
#define D_BIG_END (0<<8)
/* Byte Order */
#define D_MRR (1<<4)
/* Multiple Error Ack on SBus (readonly) */
#define D_MLE (1<<3)
/* Multiple Late Error on SBus (readonly) */
#define D_LBG (1<<2)
/* Lost Bus Grant on SBus (readonly) */
#define D_MBE (1<<1)
/* Burst Error on SBus (readonly) */
#define D_IR (1<<0)
/* Interrupt Indicator (readonly) */
/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */
#define D_ENPIO3 (1<<7)
/* Enable Pin 3 */
#define D_ENPIO2 (1<<6)
/* Enable Pin 2 */
#define D_ENPIO1 (1<<5)
/* Enable Pin 1 */
#define D_ENPIO0 (1<<4)
/* Enable Pin 0 */
#define D_ENPIO (0xf0)
/* Enable all the pins */
#define D_PIO3 (1<<3)
/* Pin 3: 1: Data mode, 0: Ctrl mode */
#define D_PIO2 (1<<2)
/* Pin 2: 1: Onboard PDN */
#define D_PIO1 (1<<1)
/* Pin 1: 0: Reset */
#define D_PIO0 (1<<0)
/* Pin 0: 1: Speakerbox PDN */
/* DBRI Commands (Page 20) */
#define D_WAIT 0x0
/* Stop execution */
#define D_PAUSE 0x1
/* Flush long pipes */
#define D_JUMP 0x2
/* New command queue */
#define D_IIQ 0x3
/* Initialize Interrupt Queue */
#define D_REX 0x4
/* Report command execution via interrupt */
#define D_SDP 0x5
/* Setup Data Pipe */
#define D_CDP 0x6
/* Continue Data Pipe (reread NULL Pointer) */
#define D_DTS 0x7
/* Define Time Slot */
#define D_SSP 0x8
/* Set short Data Pipe */
#define D_CHI 0x9
/* Set CHI Global Mode */
#define D_NT 0xa
/* NT Command */
#define D_TE 0xb
/* TE Command */
#define D_CDEC 0xc
/* Codec setup */
#define D_TEST 0xd
/* No comment */
#define D_CDM 0xe
/* CHI Data mode command */
/* Special bits for some commands */
#define D_PIPE(v) ((v)<<0)
/* Pipe Nr: 0-15 long, 16-21 short */
/* Setup Data Pipe */
/* IRM */
#define D_SDP_2SAME (1<<18)
/* Report 2nd time in a row value rcvd*/
#define D_SDP_CHANGE (2<<18)
/* Report any changes */
#define D_SDP_EVERY (3<<18)
/* Report any changes */
#define D_SDP_EOL (1<<17)
/* EOL interrupt enable */
#define D_SDP_IDLE (1<<16)
/* HDLC idle interrupt enable */
/* Pipe data MODE */
#define D_SDP_MEM (0<<13)
/* To/from memory */
#define D_SDP_HDLC (2<<13)
#define D_SDP_HDLC_D (3<<13)
/* D Channel (prio control)*/
#define D_SDP_SER (4<<13)
/* Serial to serial */
#define D_SDP_FIXED (6<<13)
/* Short only */
#define D_SDP_MODE(v) ((v)&(7<<13))
#define D_SDP_TO_SER (1<<12)
/* Direction */
#define D_SDP_FROM_SER (0<<12)
/* Direction */
#define D_SDP_MSB (1<<11)
/* Bit order within Byte */
#define D_SDP_LSB (0<<11)
/* Bit order within Byte */
#define D_SDP_P (1<<10)
/* Pointer Valid */
#define D_SDP_A (1<<8)
/* Abort */
#define D_SDP_C (1<<7)
/* Clear */
/* Define Time Slot */
#define D_DTS_VI (1<<17)
/* Valid Input Time-Slot Descriptor */
#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 */
/* Time Slot defines */
#define D_TS_LEN(v) ((v)<<24)
/* Number of bits in this time slot */
#define D_TS_CYCLE(v) ((v)<<14)
/* Bit Count at start of TS */
#define D_TS_DI (1<<13)
/* Data Invert */
#define D_TS_1CHANNEL (0<<10)
/* Single Channel / Normal mode */
#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 */
/* Concentration Highway Interface Modes */
#define D_CHI_CHICM(v) ((v)<<16)
/* Clock mode */
#define D_CHI_IR (1<<15)
/* Immediate Interrupt Report */
#define D_CHI_EN (1<<14)
/* CHIL Interrupt enabled */
#define D_CHI_OD (1<<13)
/* Open Drain Enable */
#define D_CHI_FE (1<<12)
/* Sample CHIFS on Rising Frame Edge */
#define D_CHI_FD (1<<11)
/* Frame Drive */
#define D_CHI_BPF(v) ((v)<<0)
/* Bits per Frame */
/* NT: These are here for completeness */
#define D_NT_FBIT (1<<17)
/* Frame Bit */
#define D_NT_NBF (1<<16)
/* Number of bad frames to loose framing */
#define D_NT_IRM_IMM (1<<15)
/* Interrupt Report & Mask: Immediate */
#define D_NT_IRM_EN (1<<14)
/* Interrupt Report & Mask: Enable */
#define D_NT_ISNT (1<<13)
/* Configfure interface as NT */
#define D_NT_FT (1<<12)
/* Fixed Timing */
#define D_NT_EZ (1<<11)
/* Echo Channel is Zeros */
#define D_NT_IFA (1<<10)
/* Inhibit Final Activation */
#define D_NT_ACT (1<<9)
/* Activate Interface */
#define D_NT_MFE (1<<8)
/* Multiframe Enable */
#define D_NT_RLB(v) ((v)<<5)
/* Remote Loopback */
#define D_NT_LLB(v) ((v)<<2)
/* Local Loopback */
#define D_NT_FACT (1<<1)
/* Force Activation */
#define D_NT_ABV (1<<0)
/* Activate Bipolar Violation */
/* Codec Setup */
#define D_CDEC_CK(v) ((v)<<24)
/* Clock Select */
#define D_CDEC_FED(v) ((v)<<12)
/* FSCOD Falling Edge Delay */
#define D_CDEC_RED(v) ((v)<<0)
/* FSCOD Rising Edge Delay */
/* Test */
#define D_TEST_RAM(v) ((v)<<16)
/* RAM Pointer */
#define D_TEST_SIZE(v) ((v)<<11)
/* */
#define D_TEST_ROMONOFF 0x5
/* Toggle ROM opcode monitor on/off */
#define D_TEST_PROC 0x6
/* MicroProcessor test */
#define D_TEST_SER 0x7
/* Serial-Controller test */
#define D_TEST_RAMREAD 0x8
/* Copy from Ram to system memory */
#define D_TEST_RAMWRITE 0x9
/* Copy into Ram from system memory */
#define D_TEST_RAMBIST 0xa
/* RAM Built-In Self Test */
#define D_TEST_MCBIST 0xb
/* Microcontroller Built-In Self Test */
#define D_TEST_DUMP 0xe
/* ROM Dump */
/* CHI Data Mode */
#define D_CDM_THI (1<<8)
/* Transmit Data on CHIDR Pin */
#define D_CDM_RHI (1<<7)
/* Receive Data on CHIDX Pin */
#define D_CDM_RCE (1<<6)
/* Receive on Rising Edge of CHICK */
#define D_CDM_XCE (1<<2)
/* Transmit Data on Rising Edge of CHICK */
#define D_CDM_XEN (1<<1)
/* Transmit Highway Enable */
#define D_CDM_REN (1<<0)
/* Receive Highway Enable */
/* The Interrupts */
#define D_INTR_BRDY 1
/* Buffer Ready for processing */
#define D_INTR_MINT 2
/* Marked Interrupt in RD/TD */
#define D_INTR_IBEG 3
/* Flag to idle transition detected (HDLC) */
#define D_INTR_IEND 4
/* Idle to flag transition detected (HDLC) */
#define D_INTR_EOL 5
/* End of List */
#define D_INTR_CMDI 6
/* Command has bean read */
#define D_INTR_XCMP 8
/* Transmission of frame complete */
#define D_INTR_SBRI 9
/* BRI status change info */
#define D_INTR_FXDT 10
/* Fixed data change */
#define D_INTR_CHIL 11
/* CHI lost frame sync (channel 36 only) */
#define D_INTR_COLL 11
/* Unrecoverable D-Channel collision */
#define D_INTR_DBYT 12
/* Dropped by frame slip */
#define D_INTR_RBYT 13
/* Repeated by frame slip */
#define D_INTR_LINT 14
/* Lost Interrupt */
#define D_INTR_UNDR 15
/* DMA underrun */
#define D_INTR_TE 32
#define D_INTR_NT 34
#define D_INTR_CHI 36
#define D_INTR_CMD 38
#define D_INTR_GETCHAN(v) (((v)>>24) & 0x3f)
#define D_INTR_GETCODE(v) (((v)>>20) & 0xf)
#define D_INTR_GETCMD(v) (((v)>>16) & 0xf)
#define D_INTR_GETVAL(v) ((v) & 0xffff)
#define D_INTR_GETRVAL(v) ((v) & 0xfffff)
#define D_P_0 0
/* TE receive anchor */
#define D_P_1 1
/* TE transmit anchor */
#define D_P_2 2
/* NT transmit anchor */
#define D_P_3 3
/* NT receive anchor */
#define D_P_4 4
/* CHI send data */
#define D_P_5 5
/* CHI receive data */
#define D_P_6 6
/* */
#define D_P_7 7
/* */
#define D_P_8 8
/* */
#define D_P_9 9
/* */
#define D_P_10 10
/* */
#define D_P_11 11
/* */
#define D_P_12 12
/* */
#define D_P_13 13
/* */
#define D_P_14 14
/* */
#define D_P_15 15
/* */
#define D_P_16 16
/* CHI anchor pipe */
#define D_P_17 17
/* CHI send */
#define D_P_18 18
/* CHI receive */
#define D_P_19 19
/* CHI receive */
#define D_P_20 20
/* CHI receive */
#define D_P_21 21
/* */
#define D_P_22 22
/* */
#define D_P_23 23
/* */
#define D_P_24 24
/* */
#define D_P_25 25
/* */
#define D_P_26 26
/* */
#define D_P_27 27
/* */
#define D_P_28 28
/* */
#define D_P_29 29
/* */
#define D_P_30 30
/* */
#define D_P_31 31
/* */
/* Transmit descriptor defines */
#define DBRI_TD_F (1<<31)
/* End of Frame */
#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 */
#define DBRI_TD_I (1<<13)
/* Transmit Idle Characters */
#define DBRI_TD_FCNT(v) (v)
/* Flag Count */
#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_BCNT(v) (v)
/* Buffer size */
#define DBRI_RD_CRC (1<<7)
/* 0: CRC is correct */
#define DBRI_RD_BBC (1<<6)
/* 1: Bad Byte received */
#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_ */
drivers/sbus/audio/dmy.c
deleted
100644 → 0
View file @
2ee094ef
/* $Id: dmy.c,v 1.10 2001/10/08 22:19:50 davem Exp $
* drivers/sbus/audio/dummy.c
*
* Copyright 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
*
* This is a dummy lowlevel driver. Consider it a distant cousin of
* /proc/audio; It pretends to be a piece of audio hardware, and writes
* to a file instead. (or will shortly)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <linux/delay.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/sbus.h>
#include <asm/audioio.h>
#include "dummy.h"
#define MAX_DRIVERS 1
static
struct
sparcaudio_driver
drivers
[
MAX_DRIVERS
];
static
int
num_drivers
;
static
int
dummy_play_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
);
static
int
dummy_record_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
);
static
int
dummy_output_muted
(
struct
sparcaudio_driver
*
drv
,
int
value
);
static
int
dummy_attach
(
struct
sparcaudio_driver
*
drv
)
__init
;
static
int
dummy_set_output_encoding
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
value
!=
0
)
{
dummy_chip
->
perchip_info
.
play
.
encoding
=
value
;
return
0
;
}
return
-
EINVAL
;
}
static
int
dummy_set_input_encoding
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
value
!=
0
)
{
dummy_chip
->
perchip_info
.
record
.
encoding
=
value
;
return
0
;
}
return
-
EINVAL
;
}
static
int
dummy_get_output_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
encoding
;
}
static
int
dummy_get_input_encoding
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
encoding
;
}
static
int
dummy_set_output_rate
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
value
!=
0
)
{
dummy_chip
->
perchip_info
.
play
.
sample_rate
=
value
;
return
0
;
}
return
-
EINVAL
;
}
static
int
dummy_set_input_rate
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
value
!=
0
)
{
dummy_chip
->
perchip_info
.
record
.
sample_rate
=
value
;
return
0
;
}
return
-
EINVAL
;
}
static
int
dummy_get_output_rate
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
sample_rate
;
}
static
int
dummy_get_input_rate
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
sample_rate
;
}
/* Generically we support 4 channels. This does 2 */
static
int
dummy_set_output_channels
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
switch
(
value
)
{
case
1
:
case
2
:
break
;
default:
return
-
(
EINVAL
);
};
dummy_chip
->
perchip_info
.
play
.
channels
=
value
;
return
0
;
}
/* Generically we support 4 channels. This does 2 */
static
int
dummy_set_input_channels
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
switch
(
value
)
{
case
1
:
case
2
:
break
;
default:
return
-
(
EINVAL
);
};
dummy_chip
->
perchip_info
.
record
.
channels
=
value
;
return
0
;
}
static
int
dummy_get_input_channels
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
channels
;
}
static
int
dummy_get_output_channels
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
channels
;
}
static
int
dummy_get_output_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
precision
;
}
static
int
dummy_get_input_precision
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
precision
;
}
static
int
dummy_set_output_precision
(
struct
sparcaudio_driver
*
drv
,
int
val
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
play
.
precision
=
val
;
return
dummy_chip
->
perchip_info
.
play
.
precision
;
}
static
int
dummy_set_input_precision
(
struct
sparcaudio_driver
*
drv
,
int
val
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
precision
=
val
;
return
dummy_chip
->
perchip_info
.
record
.
precision
;
}
/* Set output mute */
static
int
dummy_output_muted
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
!
value
)
dummy_chip
->
perchip_info
.
output_muted
=
0
;
else
dummy_chip
->
perchip_info
.
output_muted
=
1
;
return
0
;
}
static
int
dummy_get_output_muted
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
output_muted
;
}
static
int
dummy_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
dummy_get_output_ports
(
struct
sparcaudio_driver
*
drv
)
{
return
(
AUDIO_LINE_OUT
|
AUDIO_SPEAKER
|
AUDIO_HEADPHONE
);
}
static
int
dummy_get_input_ports
(
struct
sparcaudio_driver
*
drv
)
{
return
(
AUDIO_ANALOG_LOOPBACK
);
}
/* Set chip "output" port */
static
int
dummy_set_output_port
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
play
.
port
=
value
;
return
value
;
}
static
int
dummy_set_input_port
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
port
=
value
;
return
value
;
}
static
int
dummy_get_output_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
port
;
}
static
int
dummy_get_input_port
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
port
;
}
static
int
dummy_get_output_error
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
play
.
error
;
}
static
int
dummy_get_input_error
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
record
.
error
;
}
static
int
dummy_get_output_samples
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
samples
;
}
static
int
dummy_get_output_pause
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
play
.
pause
;
}
static
int
dummy_set_output_volume
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_play_gain
(
drv
,
value
,
dummy_chip
->
perchip_info
.
play
.
balance
);
return
0
;
}
static
int
dummy_get_output_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
play
.
gain
;
}
static
int
dummy_set_output_balance
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
play
.
balance
=
value
;
dummy_play_gain
(
drv
,
dummy_chip
->
perchip_info
.
play
.
gain
,
dummy_chip
->
perchip_info
.
play
.
balance
);
return
0
;
}
static
int
dummy_get_output_balance
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
play
.
balance
;
}
/* Set chip play gain */
static
int
dummy_play_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
int
tmp
=
0
,
r
,
l
,
r_adj
,
l_adj
;
r
=
l
=
value
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
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
==
0
)
?
(
l_adj
=
DUMMY_MAX_DEV_ATEN
)
:
(
l_adj
=
DUMMY_MAX_ATEN
-
(
l
*
(
DUMMY_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
(
r
==
0
)
?
(
r_adj
=
DUMMY_MAX_DEV_ATEN
)
:
(
r_adj
=
DUMMY_MAX_ATEN
-
(
r
*
(
DUMMY_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
if
((
value
==
0
)
||
(
value
==
AUDIO_MAX_GAIN
))
{
tmp
=
value
;
}
else
{
if
(
value
==
l
)
{
tmp
=
((
DUMMY_MAX_ATEN
-
l_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
DUMMY_MAX_ATEN
+
1
));
}
else
if
(
value
==
r
)
{
tmp
=
((
DUMMY_MAX_ATEN
-
r_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
DUMMY_MAX_ATEN
+
1
));
}
}
dummy_chip
->
perchip_info
.
play
.
gain
=
tmp
;
return
0
;
}
static
int
dummy_get_input_samples
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
samples
;
}
static
int
dummy_get_input_pause
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
record
.
pause
;
}
static
int
dummy_set_monitor_volume
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
return
0
;
}
static
int
dummy_get_monitor_volume
(
struct
sparcaudio_driver
*
drv
)
{
return
0
;
}
static
int
dummy_set_input_volume
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_record_gain
(
drv
,
value
,
dummy_chip
->
perchip_info
.
record
.
balance
);
return
0
;
}
static
int
dummy_get_input_volume
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
dummy_chip
->
perchip_info
.
record
.
gain
;
}
static
int
dummy_set_input_balance
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
balance
=
value
;
dummy_record_gain
(
drv
,
dummy_chip
->
perchip_info
.
record
.
gain
,
dummy_chip
->
perchip_info
.
play
.
balance
);
return
0
;
}
static
int
dummy_get_input_balance
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
return
(
int
)
dummy_chip
->
perchip_info
.
record
.
balance
;
}
static
int
dummy_record_gain
(
struct
sparcaudio_driver
*
drv
,
int
value
,
unsigned
char
balance
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
int
tmp
=
0
,
r
,
l
,
r_adj
,
l_adj
;
r
=
l
=
value
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
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
==
0
)
?
(
l_adj
=
DUMMY_MAX_DEV_ATEN
)
:
(
l_adj
=
DUMMY_MAX_ATEN
-
(
l
*
(
DUMMY_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
(
r
==
0
)
?
(
r_adj
=
DUMMY_MAX_DEV_ATEN
)
:
(
r_adj
=
DUMMY_MAX_ATEN
-
(
r
*
(
DUMMY_MAX_ATEN
+
1
)
/
(
AUDIO_MAX_GAIN
+
1
)));
if
((
value
==
0
)
||
(
value
==
AUDIO_MAX_GAIN
))
{
tmp
=
value
;
}
else
{
if
(
value
==
l
)
{
tmp
=
((
DUMMY_MAX_ATEN
-
l_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
DUMMY_MAX_ATEN
+
1
));
}
else
if
(
value
==
r
)
{
tmp
=
((
DUMMY_MAX_ATEN
-
r_adj
)
*
(
AUDIO_MAX_GAIN
+
1
)
/
(
DUMMY_MAX_ATEN
+
1
));
}
}
dummy_chip
->
perchip_info
.
record
.
gain
=
tmp
;
return
0
;
}
/* Reset the audio chip to a sane state. */
static
void
dummy_chip_reset
(
struct
sparcaudio_driver
*
drv
)
{
dummy_set_output_encoding
(
drv
,
AUDIO_ENCODING_ULAW
);
dummy_set_output_rate
(
drv
,
DUMMY_RATE
);
dummy_set_output_channels
(
drv
,
DUMMY_CHANNELS
);
dummy_set_output_precision
(
drv
,
DUMMY_PRECISION
);
dummy_set_output_balance
(
drv
,
AUDIO_MID_BALANCE
);
dummy_set_output_volume
(
drv
,
DUMMY_DEFAULT_PLAYGAIN
);
dummy_set_output_port
(
drv
,
AUDIO_SPEAKER
);
dummy_output_muted
(
drv
,
0
);
dummy_set_input_encoding
(
drv
,
AUDIO_ENCODING_ULAW
);
dummy_set_input_rate
(
drv
,
DUMMY_RATE
);
dummy_set_input_channels
(
drv
,
DUMMY_CHANNELS
);
dummy_set_input_precision
(
drv
,
DUMMY_PRECISION
);
dummy_set_input_balance
(
drv
,
AUDIO_MID_BALANCE
);
dummy_set_input_volume
(
drv
,
DUMMY_DEFAULT_PLAYGAIN
);
dummy_set_input_port
(
drv
,
AUDIO_SPEAKER
);
}
static
int
dummy_open
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
/* Set the default audio parameters if not already in use. */
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
if
(
!
(
drv
->
flags
&
SDF_OPEN_WRITE
)
&&
(
dummy_chip
->
perchip_info
.
play
.
active
==
0
))
{
dummy_chip
->
perchip_info
.
play
.
open
=
1
;
dummy_chip
->
perchip_info
.
play
.
samples
=
dummy_chip
->
perchip_info
.
play
.
error
=
0
;
}
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
if
(
!
(
drv
->
flags
&
SDF_OPEN_READ
)
&&
(
dummy_chip
->
perchip_info
.
record
.
active
==
0
))
{
dummy_chip
->
perchip_info
.
record
.
open
=
1
;
dummy_chip
->
perchip_info
.
record
.
samples
=
dummy_chip
->
perchip_info
.
record
.
error
=
0
;
}
}
MOD_INC_USE_COUNT
;
return
0
;
}
static
void
dummy_release
(
struct
inode
*
inode
,
struct
file
*
file
,
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
file
->
f_mode
&
FMODE_WRITE
)
{
dummy_chip
->
perchip_info
.
play
.
active
=
dummy_chip
->
perchip_info
.
play
.
open
=
0
;
}
if
(
file
->
f_mode
&
FMODE_READ
)
{
dummy_chip
->
perchip_info
.
record
.
active
=
dummy_chip
->
perchip_info
.
record
.
open
=
0
;
}
MOD_DEC_USE_COUNT
;
}
static
void
dummy_output_done_task
(
void
*
arg
)
{
struct
sparcaudio_driver
*
drv
=
(
struct
sparcaudio_driver
*
)
arg
;
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
sparcaudio_output_done
(
drv
,
1
);
if
(
dummy_chip
->
perchip_info
.
record
.
active
)
sparcaudio_input_done
(
drv
,
1
);
}
static
void
dummy_start_output
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
if
(
dummy_chip
->
perchip_info
.
play
.
pause
||
!
count
)
return
;
dummy_chip
->
perchip_info
.
play
.
active
=
1
;
/* fake an "interrupt" to deal with this block */
INIT_LIST_HEAD
(
&
dummy_chip
->
tqueue
.
list
);
dummy_chip
->
tqueue
.
sync
=
0
;
dummy_chip
->
tqueue
.
routine
=
dummy_output_done_task
;
dummy_chip
->
tqueue
.
data
=
drv
;
queue_task
(
&
dummy_chip
->
tqueue
,
&
tq_immediate
);
mark_bh
(
IMMEDIATE_BH
);
}
static
void
dummy_start_input
(
struct
sparcaudio_driver
*
drv
,
__u8
*
buffer
,
unsigned
long
count
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
active
=
1
;
}
static
void
dummy_stop_output
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
play
.
active
=
0
;
}
static
void
dummy_stop_input
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
active
=
0
;
}
static
int
dummy_set_output_pause
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
play
.
pause
=
value
;
if
(
!
value
)
sparcaudio_output_done
(
drv
,
0
);
return
value
;
}
static
int
dummy_set_input_pause
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
dummy_chip
->
perchip_info
.
record
.
pause
=
value
;
/* This should probably cause play pause. */
return
value
;
}
static
int
dummy_set_input_error
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
return
0
;
}
static
int
dummy_set_output_error
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
int
i
;
i
=
dummy_chip
->
perchip_info
.
play
.
error
;
dummy_chip
->
perchip_info
.
play
.
error
=
value
;
return
i
;
}
static
int
dummy_set_output_samples
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
int
i
;
i
=
dummy_chip
->
perchip_info
.
play
.
samples
;
dummy_chip
->
perchip_info
.
play
.
samples
=
value
;
return
i
;
}
static
int
dummy_set_input_samples
(
struct
sparcaudio_driver
*
drv
,
int
value
)
{
struct
dummy_chip
*
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
int
i
;
i
=
dummy_chip
->
perchip_info
.
play
.
samples
;
dummy_chip
->
perchip_info
.
record
.
samples
=
value
;
return
i
;
}
/* In order to fake things which care out, play we're a 4231 */
static
void
dummy_audio_getdev
(
struct
sparcaudio_driver
*
drv
,
audio_device_t
*
audinfo
)
{
strncpy
(
audinfo
->
name
,
"SUNW,cs4231"
,
sizeof
(
audinfo
->
name
)
-
1
);
strncpy
(
audinfo
->
version
,
"a"
,
sizeof
(
audinfo
->
version
)
-
1
);
strncpy
(
audinfo
->
config
,
"onboard1"
,
sizeof
(
audinfo
->
config
)
-
1
);
}
static
int
dummy_audio_getdev_sunos
(
struct
sparcaudio_driver
*
drv
)
{
return
5
;
}
static
struct
sparcaudio_operations
dummy_ops
=
{
dummy_open
,
dummy_release
,
NULL
,
dummy_start_output
,
dummy_stop_output
,
dummy_start_input
,
dummy_stop_input
,
dummy_audio_getdev
,
dummy_set_output_volume
,
dummy_get_output_volume
,
dummy_set_input_volume
,
dummy_get_input_volume
,
dummy_set_monitor_volume
,
dummy_get_monitor_volume
,
dummy_set_output_balance
,
dummy_get_output_balance
,
dummy_set_input_balance
,
dummy_get_input_balance
,
dummy_set_output_channels
,
dummy_get_output_channels
,
dummy_set_input_channels
,
dummy_get_input_channels
,
dummy_set_output_precision
,
dummy_get_output_precision
,
dummy_set_input_precision
,
dummy_get_input_precision
,
dummy_set_output_port
,
dummy_get_output_port
,
dummy_set_input_port
,
dummy_get_input_port
,
dummy_set_output_encoding
,
dummy_get_output_encoding
,
dummy_set_input_encoding
,
dummy_get_input_encoding
,
dummy_set_output_rate
,
dummy_get_output_rate
,
dummy_set_input_rate
,
dummy_get_input_rate
,
dummy_audio_getdev_sunos
,
dummy_get_output_ports
,
dummy_get_input_ports
,
dummy_output_muted
,
dummy_get_output_muted
,
dummy_set_output_pause
,
dummy_get_output_pause
,
dummy_set_input_pause
,
dummy_get_input_pause
,
dummy_set_output_samples
,
dummy_get_output_samples
,
dummy_set_input_samples
,
dummy_get_input_samples
,
dummy_set_output_error
,
dummy_get_output_error
,
dummy_set_input_error
,
dummy_get_input_error
,
dummy_get_formats
,
};
/* Attach to an dummy chip given its PROM node. */
static
int
__init
dummy_attach
(
struct
sparcaudio_driver
*
drv
)
{
struct
dummy_chip
*
dummy_chip
;
int
err
;
/* Allocate our private information structure. */
drv
->
private
=
kmalloc
(
sizeof
(
struct
dummy_chip
),
GFP_KERNEL
);
if
(
drv
->
private
==
NULL
)
return
-
ENOMEM
;
/* Point at the information structure and initialize it. */
drv
->
ops
=
&
dummy_ops
;
dummy_chip
=
(
struct
dummy_chip
*
)
drv
->
private
;
/* Reset parameters. */
dummy_chip_reset
(
drv
);
/* Register ourselves with the midlevel audio driver. */
err
=
register_sparcaudio_driver
(
drv
,
2
);
if
(
err
<
0
)
{
printk
(
KERN_ERR
"dummy: unable to register
\n
"
);
kfree
(
drv
->
private
);
return
-
EIO
;
}
dummy_chip
->
perchip_info
.
play
.
active
=
dummy_chip
->
perchip_info
.
play
.
pause
=
0
;
dummy_chip
->
perchip_info
.
play
.
avail_ports
=
(
AUDIO_HEADPHONE
|
AUDIO_SPEAKER
|
AUDIO_LINE_OUT
);
/* Announce the hardware to the user. */
printk
(
KERN_INFO
"audio%d: dummy at 0x0 irq 0
\n
"
,
drv
->
index
);
/* Success! */
return
0
;
}
/* Detach from an dummy chip given the device structure. */
static
void
__exit
dummy_detach
(
struct
sparcaudio_driver
*
drv
)
{
unregister_sparcaudio_driver
(
drv
,
2
);
kfree
(
drv
->
private
);
}
/* Probe for the dummy chip and then attach the driver. */
static
int
__init
dummy_init
(
void
)
{
num_drivers
=
0
;
/* Add support here for specifying multiple dummies to attach at once. */
if
(
dummy_attach
(
&
drivers
[
num_drivers
])
==
0
)
num_drivers
++
;
/* Only return success if we found some dummy chips. */
return
(
num_drivers
>
0
)
?
0
:
-
EIO
;
}
static
void
__exit
dummy_exit
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
num_drivers
;
i
++
)
{
dummy_detach
(
&
drivers
[
i
]);
num_drivers
--
;
}
}
module_init
(
dummy_init
);
module_exit
(
dummy_exit
);
MODULE_LICENSE
(
"GPL"
);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
drivers/sbus/audio/dummy.h
deleted
100644 → 0
View file @
2ee094ef
/* $Id: dummy.h,v 1.3 1999/09/21 14:37:41 davem Exp $
* drivers/sbus/audio/dummy.h
*
* Copyright (C) 1998 Derrick J. Brashear (shadow@dementia.org)
*/
#ifndef _DUMMY_H_
#define _DUMMY_H_
#include <linux/types.h>
#include <linux/tqueue.h>
#define DUMMY_OUTFILE "/usr/tmp/dummy.au"
/* Our structure for each chip */
struct
dummy_chip
{
struct
audio_info
perchip_info
;
unsigned
int
playlen
;
struct
tq_struct
tqueue
;
};
#define DUMMY_MIN_ATEN (0)
#define DUMMY_MAX_ATEN (31)
#define DUMMY_MAX_DEV_ATEN (63)
#define DUMMY_MON_MIN_ATEN (0)
#define DUMMY_MON_MAX_ATEN (63)
#define DUMMY_DEFAULT_PLAYGAIN (132)
#define DUMMY_DEFAULT_RECGAIN (126)
#define DUMMY_MIN_GAIN (0)
#define DUMMY_MAX_GAIN (15)
#define DUMMY_PRECISION (8)
/* # of bits/sample */
#define DUMMY_CHANNELS (1)
/* channels/sample */
#define DUMMY_RATE (8000)
/* default sample rate */
#endif
/* _DUMMY_H_ */
include/asm-sparc/audioio.h
View file @
21f59c57
...
...
@@ -231,414 +231,4 @@ typedef struct audio_device {
*/
#define AUDIO_DIAG_LOOPBACK _IOW('A', 101, int)
/*
* Linux kernel internal implementation.
*/
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#define SDF_OPEN_WRITE 0x00000001
#define SDF_OPEN_READ 0x00000002
struct
sparcaudio_ringbuffer
{
__u8
*
rb_start
,
*
rb_end
;
/* start, end of this memory buffer */
__u8
*
rb_in
,
*
rb_out
;
/* input, output pointers */
int
rb_fragsize
;
/* size of an audio frag */
int
rb_numfrags
;
/* number of frags */
int
rb_count
,
rb_hiwat
,
rb_lowat
;
/* bytes in use, hi/lo wat points */
int
rb_bufsize
;
/* total size of buffer */
};
struct
sparcaudio_driver
{
const
char
*
name
;
struct
sparcaudio_operations
*
ops
;
void
*
private
;
unsigned
long
flags
;
struct
strevent
*
sd_siglist
;
/* duplex: 0=simplex, 1=duplex, 2=loop */
int
sd_sigflags
,
duplex
;
/* Which audio device are we? */
int
index
;
/* This device */
struct
sbus_dev
*
dev
;
/* Processes blocked on open() sit here. */
wait_queue_head_t
open_wait
;
/* Task queue for this driver's bottom half. */
struct
tq_struct
tqueue
;
/* Start of ring buffer support */
__u8
*
input_buffer
,
*
output_buffer
;
/* Support for a circular queue of output buffers. */
__u8
**
output_buffers
;
size_t
*
output_sizes
,
output_size
,
output_buffer_size
;
int
num_output_buffers
,
output_front
,
output_rear
,
output_offset
;
int
output_count
,
output_active
,
playing_count
,
output_eof
;
wait_queue_head_t
output_write_wait
,
output_drain_wait
;
char
*
output_notify
;
/* Support for a circular queue of input buffers. */
__u8
**
input_buffers
;
size_t
*
input_sizes
,
input_size
,
input_buffer_size
;
int
num_input_buffers
,
input_front
,
input_rear
,
input_offset
;
int
input_count
,
input_active
,
recording_count
;
wait_queue_head_t
input_read_wait
;
/* Hack to make it look like we support variable size buffers. */
int
buffer_size
;
int
mixer_modify_counter
;
};
struct
sparcaudio_operations
{
int
(
*
open
)(
struct
inode
*
,
struct
file
*
,
struct
sparcaudio_driver
*
);
void
(
*
release
)(
struct
inode
*
,
struct
file
*
,
struct
sparcaudio_driver
*
);
int
(
*
ioctl
)(
struct
inode
*
,
struct
file
*
,
unsigned
int
,
unsigned
long
,
struct
sparcaudio_driver
*
);
/* Ask driver to begin playing a buffer. */
void
(
*
start_output
)(
struct
sparcaudio_driver
*
,
__u8
*
,
unsigned
long
);
/* Ask driver to stop playing a buffer. */
void
(
*
stop_output
)(
struct
sparcaudio_driver
*
);
/* Ask driver to begin recording into a buffer. */
void
(
*
start_input
)(
struct
sparcaudio_driver
*
,
__u8
*
,
unsigned
long
);
/* Ask driver to stop recording. */
void
(
*
stop_input
)(
struct
sparcaudio_driver
*
);
/* Return driver name/version to caller. (/dev/audio specific) */
void
(
*
sunaudio_getdev
)(
struct
sparcaudio_driver
*
,
audio_device_t
*
);
/* Get and set the output volume. (0-255) */
int
(
*
set_output_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the input volume. (0-255) */
int
(
*
set_input_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the monitor volume. (0-255) */
int
(
*
set_monitor_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_monitor_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the output balance. (0-64) */
int
(
*
set_output_balance
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_balance
)(
struct
sparcaudio_driver
*
);
/* Get and set the input balance. (0-64) */
int
(
*
set_input_balance
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_balance
)(
struct
sparcaudio_driver
*
);
/* Get and set the output channels. (1-4) */
int
(
*
set_output_channels
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_channels
)(
struct
sparcaudio_driver
*
);
/* Get and set the input channels. (1-4) */
int
(
*
set_input_channels
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_channels
)(
struct
sparcaudio_driver
*
);
/* Get and set the output precision. (8-32) */
int
(
*
set_output_precision
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_precision
)(
struct
sparcaudio_driver
*
);
/* Get and set the input precision. (8-32) */
int
(
*
set_input_precision
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_precision
)(
struct
sparcaudio_driver
*
);
/* Get and set the output port. () */
int
(
*
set_output_port
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_port
)(
struct
sparcaudio_driver
*
);
/* Get and set the input port. () */
int
(
*
set_input_port
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_port
)(
struct
sparcaudio_driver
*
);
/* Get and set the output encoding. () */
int
(
*
set_output_encoding
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_encoding
)(
struct
sparcaudio_driver
*
);
/* Get and set the input encoding. () */
int
(
*
set_input_encoding
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_encoding
)(
struct
sparcaudio_driver
*
);
/* Get and set the output rate. () */
int
(
*
set_output_rate
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_rate
)(
struct
sparcaudio_driver
*
);
/* Get and set the input rate. () */
int
(
*
set_input_rate
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_rate
)(
struct
sparcaudio_driver
*
);
/* Return driver number to caller. (SunOS /dev/audio specific) */
int
(
*
sunaudio_getdev_sunos
)(
struct
sparcaudio_driver
*
);
/* Get available ports */
int
(
*
get_output_ports
)(
struct
sparcaudio_driver
*
);
int
(
*
get_input_ports
)(
struct
sparcaudio_driver
*
);
/* Get and set output mute */
int
(
*
set_output_muted
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_muted
)(
struct
sparcaudio_driver
*
);
/* Get and set output pause */
int
(
*
set_output_pause
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_pause
)(
struct
sparcaudio_driver
*
);
/* Get and set input pause */
int
(
*
set_input_pause
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_pause
)(
struct
sparcaudio_driver
*
);
/* Get and set output samples */
int
(
*
set_output_samples
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_samples
)(
struct
sparcaudio_driver
*
);
/* Get and set input samples */
int
(
*
set_input_samples
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_samples
)(
struct
sparcaudio_driver
*
);
/* Get and set output error */
int
(
*
set_output_error
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_error
)(
struct
sparcaudio_driver
*
);
/* Get and set input error */
int
(
*
set_input_error
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_error
)(
struct
sparcaudio_driver
*
);
/* Get supported encodings */
int
(
*
get_formats
)(
struct
sparcaudio_driver
*
);
};
extern
int
register_sparcaudio_driver
(
struct
sparcaudio_driver
*
,
int
);
extern
int
unregister_sparcaudio_driver
(
struct
sparcaudio_driver
*
,
int
);
extern
void
sparcaudio_output_done
(
struct
sparcaudio_driver
*
,
int
);
extern
void
sparcaudio_input_done
(
struct
sparcaudio_driver
*
,
int
);
#endif
/* Device minor numbers */
#define SPARCAUDIO_MIXER_MINOR 0
/* No sequencer (1) */
/* No midi (2) */
#define SPARCAUDIO_DSP_MINOR 3
#define SPARCAUDIO_AUDIO_MINOR 4
#define SPARCAUDIO_DSP16_MINOR 5
#define SPARCAUDIO_STATUS_MINOR 6
#define SPARCAUDIO_AUDIOCTL_MINOR 7
/* No sequencer l2 (8) */
/* No sound processor (9) */
/* allocate 2^SPARCAUDIO_DEVICE_SHIFT minors per audio device */
#define SPARCAUDIO_DEVICE_SHIFT 4
/* With the coming of dummy devices this should perhaps be as high as 5? */
#define SPARCAUDIO_MAX_DEVICES 3
/* Streams crap for realaudio */
typedef
struct
strevent
{
struct
strevent
*
se_next
;
/* next event for this stream or NULL*/
struct
strevent
*
se_prev
;
/* previous event for this stream or last
* event if this is the first one*/
pid_t
se_pid
;
/* process to be signaled */
short
se_evs
;
/* events wanted */
}
strevent_t
;
typedef
struct
stdata
{
struct
stdata
*
sd_next
;
/* all stdatas are linked together */
struct
stdata
*
sd_prev
;
struct
strevent
*
sd_siglist
;
/* processes to be sent SIGPOLL */
int
sd_sigflags
;
/* logical OR of all siglist events */
}
stdata_t
;
#define I_NREAD _IOR('S',01, int)
#define I_NREAD_SOLARIS (('S'<<8)|1)
#define I_FLUSH _IO('S',05)
#define I_FLUSH_SOLARIS (('S'<<8)|5)
#define FLUSHR 1
/* flush read queue */
#define FLUSHW 2
/* flush write queue */
#define FLUSHRW 3
/* flush both queues */
#define I_SETSIG _IO('S',011)
#define I_SETSIG_SOLARIS (('S'<<8)|11)
#define S_INPUT 0x01
#define S_HIPRI 0x02
#define S_OUTPUT 0x04
#define S_MSG 0x08
#define S_ERROR 0x0010
#define S_HANGUP 0x0020
#define S_RDNORM 0x0040
#define S_WRNORM S_OUTPUT
#define S_RDBAND 0x0080
#define S_WRBAND 0x0100
#define S_BANDURG 0x0200
#define S_ALL 0x03FF
#define I_GETSIG _IOR('S',012,int)
#define I_GETSIG_SOLARIS (('S'<<8)|12)
/* Conversion between Sun and OSS volume settings */
static
__inline__
int
OSS_LEFT
(
int
value
)
{
return
((
value
&
0xff
)
%
101
);
}
static
__inline__
int
OSS_RIGHT
(
int
value
)
{
return
(((
value
>>
8
)
&
0xff
)
%
101
);
}
static
__inline__
int
O_TO_S
(
int
value
)
{
return
value
*
255
/
100
;
}
static
__inline__
int
S_TO_O
(
int
value
)
{
return
value
*
100
/
255
;
}
static
__inline__
int
OSS_TO_GAIN
(
int
value
)
{
int
l
=
O_TO_S
(
OSS_LEFT
(
value
));
int
r
=
O_TO_S
(
OSS_RIGHT
(
value
));
return
((
l
>
r
)
?
l
:
r
);
}
static
__inline__
int
OSS_TO_LGAIN
(
int
value
)
{
int
l
=
O_TO_S
(
OSS_LEFT
(
value
));
int
r
=
O_TO_S
(
OSS_RIGHT
(
value
));
return
((
l
<
r
)
?
l
:
r
);
}
static
__inline__
int
OSS_TO_BAL
(
int
value
)
{
if
(
!
OSS_TO_GAIN
(
value
))
return
AUDIO_MID_BALANCE
;
if
(
!
OSS_TO_LGAIN
(
value
))
{
if
(
OSS_TO_GAIN
(
value
)
==
OSS_TO_GAIN
(
OSS_RIGHT
(
value
)))
return
AUDIO_RIGHT_BALANCE
;
else
return
AUDIO_LEFT_BALANCE
;
}
if
(
OSS_TO_GAIN
(
value
)
==
OSS_TO_GAIN
(
OSS_RIGHT
(
value
)))
return
((
OSS_TO_GAIN
(
value
)
-
OSS_TO_LGAIN
(
value
))
>>
AUDIO_BALANCE_SHIFT
)
+
AUDIO_MID_BALANCE
;
else
return
AUDIO_MID_BALANCE
-
((
OSS_TO_GAIN
(
value
)
-
OSS_TO_LGAIN
(
value
))
>>
AUDIO_BALANCE_SHIFT
);
}
static
__inline__
int
BAL_TO_OSS
(
int
value
,
unsigned
char
balance
)
{
int
l
,
r
,
adj
;
if
(
balance
>
63
)
balance
=
63
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
l
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
adj
=
((
AUDIO_MID_BALANCE
-
balance
)
<<
AUDIO_BALANCE_SHIFT
);
if
(
adj
<
value
)
r
=
(
int
)(
value
-
adj
)
*
100
/
255
;
else
r
=
0
;
}
else
if
(
balance
>
AUDIO_MID_BALANCE
)
{
r
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
adj
=
((
balance
-
AUDIO_MID_BALANCE
)
<<
AUDIO_BALANCE_SHIFT
);
if
(
adj
<
value
)
l
=
(
int
)(
value
-
adj
)
*
100
/
255
;
else
l
=
0
;
}
else
{
l
=
r
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
}
return
((
r
<<
8
)
+
l
);
}
#ifdef __KERNEL__
/* OSS mixer ioctl port handling */
static
__inline__
int
OSS_PORT_AUDIO
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
set
)
{
int
p
;
if
(
drv
->
ops
->
get_output_port
)
{
p
=
drv
->
ops
->
get_output_port
(
drv
);
if
(
p
&
set
)
return
0x6464
;
}
return
0
;
}
static
__inline__
int
OSS_IPORT_AUDIO
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
set
)
{
int
p
;
if
(
drv
->
ops
->
get_input_port
)
{
p
=
drv
->
ops
->
get_input_port
(
drv
);
if
(
p
&
set
)
return
0x6464
;
}
return
0
;
}
static
__inline__
void
OSS_TWIDDLE_PORT
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
ioctl
,
unsigned
int
port
,
unsigned
int
set
,
unsigned
int
value
)
{
if
(
ioctl
==
port
)
{
int
p
;
if
(
drv
->
ops
->
get_output_port
&&
drv
->
ops
->
set_output_port
)
{
p
=
drv
->
ops
->
get_output_port
(
drv
);
if
((
value
==
0
)
||
((
p
&
set
)
&&
(
OSS_LEFT
(
value
)
<
100
)))
drv
->
ops
->
set_output_port
(
drv
,
p
&
~
(
set
));
else
drv
->
ops
->
set_output_port
(
drv
,
p
|
set
);
}
}
}
static
__inline__
void
OSS_TWIDDLE_IPORT
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
ioctl
,
unsigned
int
port
,
unsigned
int
set
,
unsigned
int
value
)
{
if
(
ioctl
==
port
)
{
int
p
;
if
(
drv
->
ops
->
get_input_port
&&
drv
->
ops
->
set_input_port
)
{
p
=
drv
->
ops
->
get_input_port
(
drv
);
if
((
value
==
0
)
||
((
p
&
set
)
&&
(
OSS_LEFT
(
value
)
<
100
)))
drv
->
ops
->
set_input_port
(
drv
,
p
&
~
(
set
));
else
drv
->
ops
->
set_input_port
(
drv
,
p
|
set
);
}
}
}
#endif
/* __KERNEL__ */
#endif
/* _AUDIOIO_H_ */
include/asm-sparc64/audioio.h
View file @
21f59c57
...
...
@@ -231,414 +231,4 @@ typedef struct audio_device {
*/
#define AUDIO_DIAG_LOOPBACK _IOW('A', 101, int)
/*
* Linux kernel internal implementation.
*/
#ifdef __KERNEL__
#include <linux/fs.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#define SDF_OPEN_WRITE 0x00000001
#define SDF_OPEN_READ 0x00000002
struct
sparcaudio_ringbuffer
{
__u8
*
rb_start
,
*
rb_end
;
/* start, end of this memory buffer */
__u8
*
rb_in
,
*
rb_out
;
/* input, output pointers */
int
rb_fragsize
;
/* size of an audio frag */
int
rb_numfrags
;
/* number of frags */
int
rb_count
,
rb_hiwat
,
rb_lowat
;
/* bytes in use, hi/lo wat points */
int
rb_bufsize
;
/* total size of buffer */
};
struct
sparcaudio_driver
{
const
char
*
name
;
struct
sparcaudio_operations
*
ops
;
void
*
private
;
unsigned
long
flags
;
struct
strevent
*
sd_siglist
;
/* duplex: 0=simplex, 1=duplex, 2=loop */
int
sd_sigflags
,
duplex
;
/* Which audio device are we? */
int
index
;
/* This device */
struct
sbus_dev
*
dev
;
/* Processes blocked on open() sit here. */
wait_queue_head_t
open_wait
;
/* Task queue for this driver's bottom half. */
struct
tq_struct
tqueue
;
/* Start of ring buffer support */
__u8
*
input_buffer
,
*
output_buffer
;
/* Support for a circular queue of output buffers. */
__u8
**
output_buffers
;
size_t
*
output_sizes
,
output_size
,
output_buffer_size
;
int
num_output_buffers
,
output_front
,
output_rear
,
output_offset
;
int
output_count
,
output_active
,
playing_count
,
output_eof
;
wait_queue_head_t
output_write_wait
,
output_drain_wait
;
char
*
output_notify
;
/* Support for a circular queue of input buffers. */
__u8
**
input_buffers
;
size_t
*
input_sizes
,
input_size
,
input_buffer_size
;
int
num_input_buffers
,
input_front
,
input_rear
,
input_offset
;
int
input_count
,
input_active
,
recording_count
;
wait_queue_head_t
input_read_wait
;
/* Hack to make it look like we support variable size buffers. */
int
buffer_size
;
int
mixer_modify_counter
;
};
struct
sparcaudio_operations
{
int
(
*
open
)(
struct
inode
*
,
struct
file
*
,
struct
sparcaudio_driver
*
);
void
(
*
release
)(
struct
inode
*
,
struct
file
*
,
struct
sparcaudio_driver
*
);
int
(
*
ioctl
)(
struct
inode
*
,
struct
file
*
,
unsigned
int
,
unsigned
long
,
struct
sparcaudio_driver
*
);
/* Ask driver to begin playing a buffer. */
void
(
*
start_output
)(
struct
sparcaudio_driver
*
,
__u8
*
,
unsigned
long
);
/* Ask driver to stop playing a buffer. */
void
(
*
stop_output
)(
struct
sparcaudio_driver
*
);
/* Ask driver to begin recording into a buffer. */
void
(
*
start_input
)(
struct
sparcaudio_driver
*
,
__u8
*
,
unsigned
long
);
/* Ask driver to stop recording. */
void
(
*
stop_input
)(
struct
sparcaudio_driver
*
);
/* Return driver name/version to caller. (/dev/audio specific) */
void
(
*
sunaudio_getdev
)(
struct
sparcaudio_driver
*
,
audio_device_t
*
);
/* Get and set the output volume. (0-255) */
int
(
*
set_output_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the input volume. (0-255) */
int
(
*
set_input_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the monitor volume. (0-255) */
int
(
*
set_monitor_volume
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_monitor_volume
)(
struct
sparcaudio_driver
*
);
/* Get and set the output balance. (0-64) */
int
(
*
set_output_balance
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_balance
)(
struct
sparcaudio_driver
*
);
/* Get and set the input balance. (0-64) */
int
(
*
set_input_balance
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_balance
)(
struct
sparcaudio_driver
*
);
/* Get and set the output channels. (1-4) */
int
(
*
set_output_channels
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_channels
)(
struct
sparcaudio_driver
*
);
/* Get and set the input channels. (1-4) */
int
(
*
set_input_channels
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_channels
)(
struct
sparcaudio_driver
*
);
/* Get and set the output precision. (8-32) */
int
(
*
set_output_precision
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_precision
)(
struct
sparcaudio_driver
*
);
/* Get and set the input precision. (8-32) */
int
(
*
set_input_precision
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_precision
)(
struct
sparcaudio_driver
*
);
/* Get and set the output port. () */
int
(
*
set_output_port
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_port
)(
struct
sparcaudio_driver
*
);
/* Get and set the input port. () */
int
(
*
set_input_port
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_port
)(
struct
sparcaudio_driver
*
);
/* Get and set the output encoding. () */
int
(
*
set_output_encoding
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_encoding
)(
struct
sparcaudio_driver
*
);
/* Get and set the input encoding. () */
int
(
*
set_input_encoding
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_encoding
)(
struct
sparcaudio_driver
*
);
/* Get and set the output rate. () */
int
(
*
set_output_rate
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_rate
)(
struct
sparcaudio_driver
*
);
/* Get and set the input rate. () */
int
(
*
set_input_rate
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_rate
)(
struct
sparcaudio_driver
*
);
/* Return driver number to caller. (SunOS /dev/audio specific) */
int
(
*
sunaudio_getdev_sunos
)(
struct
sparcaudio_driver
*
);
/* Get available ports */
int
(
*
get_output_ports
)(
struct
sparcaudio_driver
*
);
int
(
*
get_input_ports
)(
struct
sparcaudio_driver
*
);
/* Get and set output mute */
int
(
*
set_output_muted
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_muted
)(
struct
sparcaudio_driver
*
);
/* Get and set output pause */
int
(
*
set_output_pause
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_pause
)(
struct
sparcaudio_driver
*
);
/* Get and set input pause */
int
(
*
set_input_pause
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_pause
)(
struct
sparcaudio_driver
*
);
/* Get and set output samples */
int
(
*
set_output_samples
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_samples
)(
struct
sparcaudio_driver
*
);
/* Get and set input samples */
int
(
*
set_input_samples
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_samples
)(
struct
sparcaudio_driver
*
);
/* Get and set output error */
int
(
*
set_output_error
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_output_error
)(
struct
sparcaudio_driver
*
);
/* Get and set input error */
int
(
*
set_input_error
)(
struct
sparcaudio_driver
*
,
int
);
int
(
*
get_input_error
)(
struct
sparcaudio_driver
*
);
/* Get supported encodings */
int
(
*
get_formats
)(
struct
sparcaudio_driver
*
);
};
extern
int
register_sparcaudio_driver
(
struct
sparcaudio_driver
*
,
int
);
extern
int
unregister_sparcaudio_driver
(
struct
sparcaudio_driver
*
,
int
);
extern
void
sparcaudio_output_done
(
struct
sparcaudio_driver
*
,
int
);
extern
void
sparcaudio_input_done
(
struct
sparcaudio_driver
*
,
int
);
#endif
/* Device minor numbers */
#define SPARCAUDIO_MIXER_MINOR 0
/* No sequencer (1) */
/* No midi (2) */
#define SPARCAUDIO_DSP_MINOR 3
#define SPARCAUDIO_AUDIO_MINOR 4
#define SPARCAUDIO_DSP16_MINOR 5
#define SPARCAUDIO_STATUS_MINOR 6
#define SPARCAUDIO_AUDIOCTL_MINOR 7
/* No sequencer l2 (8) */
/* No sound processor (9) */
/* allocate 2^SPARCAUDIO_DEVICE_SHIFT minors per audio device */
#define SPARCAUDIO_DEVICE_SHIFT 4
/* With the coming of dummy devices this should perhaps be as high as 5? */
#define SPARCAUDIO_MAX_DEVICES 3
/* Streams crap for realaudio */
typedef
struct
strevent
{
struct
strevent
*
se_next
;
/* next event for this stream or NULL*/
struct
strevent
*
se_prev
;
/* previous event for this stream or last
* event if this is the first one*/
pid_t
se_pid
;
/* process to be signaled */
short
se_evs
;
/* events wanted */
}
strevent_t
;
typedef
struct
stdata
{
struct
stdata
*
sd_next
;
/* all stdatas are linked together */
struct
stdata
*
sd_prev
;
struct
strevent
*
sd_siglist
;
/* processes to be sent SIGPOLL */
int
sd_sigflags
;
/* logical OR of all siglist events */
}
stdata_t
;
#define I_NREAD _IOR('S',01, int)
#define I_NREAD_SOLARIS (('S'<<8)|1)
#define I_FLUSH _IO('S',05)
#define I_FLUSH_SOLARIS (('S'<<8)|5)
#define FLUSHR 1
/* flush read queue */
#define FLUSHW 2
/* flush write queue */
#define FLUSHRW 3
/* flush both queues */
#define I_SETSIG _IO('S',011)
#define I_SETSIG_SOLARIS (('S'<<8)|11)
#define S_INPUT 0x01
#define S_HIPRI 0x02
#define S_OUTPUT 0x04
#define S_MSG 0x08
#define S_ERROR 0x0010
#define S_HANGUP 0x0020
#define S_RDNORM 0x0040
#define S_WRNORM S_OUTPUT
#define S_RDBAND 0x0080
#define S_WRBAND 0x0100
#define S_BANDURG 0x0200
#define S_ALL 0x03FF
#define I_GETSIG _IOR('S',012,int)
#define I_GETSIG_SOLARIS (('S'<<8)|12)
/* Conversion between Sun and OSS volume settings */
static
__inline__
int
OSS_LEFT
(
int
value
)
{
return
((
value
&
0xff
)
%
101
);
}
static
__inline__
int
OSS_RIGHT
(
int
value
)
{
return
(((
value
>>
8
)
&
0xff
)
%
101
);
}
static
__inline__
int
O_TO_S
(
int
value
)
{
return
value
*
255
/
100
;
}
static
__inline__
int
S_TO_O
(
int
value
)
{
return
value
*
100
/
255
;
}
static
__inline__
int
OSS_TO_GAIN
(
int
value
)
{
int
l
=
O_TO_S
(
OSS_LEFT
(
value
));
int
r
=
O_TO_S
(
OSS_RIGHT
(
value
));
return
((
l
>
r
)
?
l
:
r
);
}
static
__inline__
int
OSS_TO_LGAIN
(
int
value
)
{
int
l
=
O_TO_S
(
OSS_LEFT
(
value
));
int
r
=
O_TO_S
(
OSS_RIGHT
(
value
));
return
((
l
<
r
)
?
l
:
r
);
}
static
__inline__
int
OSS_TO_BAL
(
int
value
)
{
if
(
!
OSS_TO_GAIN
(
value
))
return
AUDIO_MID_BALANCE
;
if
(
!
OSS_TO_LGAIN
(
value
))
{
if
(
OSS_TO_GAIN
(
value
)
==
OSS_TO_GAIN
(
OSS_RIGHT
(
value
)))
return
AUDIO_RIGHT_BALANCE
;
else
return
AUDIO_LEFT_BALANCE
;
}
if
(
OSS_TO_GAIN
(
value
)
==
OSS_TO_GAIN
(
OSS_RIGHT
(
value
)))
return
((
OSS_TO_GAIN
(
value
)
-
OSS_TO_LGAIN
(
value
))
>>
AUDIO_BALANCE_SHIFT
)
+
AUDIO_MID_BALANCE
;
else
return
AUDIO_MID_BALANCE
-
((
OSS_TO_GAIN
(
value
)
-
OSS_TO_LGAIN
(
value
))
>>
AUDIO_BALANCE_SHIFT
);
}
static
__inline__
int
BAL_TO_OSS
(
int
value
,
unsigned
char
balance
)
{
int
l
,
r
,
adj
;
if
(
balance
>
63
)
balance
=
63
;
if
(
balance
<
AUDIO_MID_BALANCE
)
{
l
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
adj
=
((
AUDIO_MID_BALANCE
-
balance
)
<<
AUDIO_BALANCE_SHIFT
);
if
(
adj
<
value
)
r
=
(
int
)(
value
-
adj
)
*
100
/
255
;
else
r
=
0
;
}
else
if
(
balance
>
AUDIO_MID_BALANCE
)
{
r
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
adj
=
((
balance
-
AUDIO_MID_BALANCE
)
<<
AUDIO_BALANCE_SHIFT
);
if
(
adj
<
value
)
l
=
(
int
)(
value
-
adj
)
*
100
/
255
;
else
l
=
0
;
}
else
{
l
=
r
=
(
int
)
value
*
100
/
255
+
((
value
*
100
%
255
)
>
0
);
}
return
((
r
<<
8
)
+
l
);
}
#ifdef __KERNEL__
/* OSS mixer ioctl port handling */
static
__inline__
int
OSS_PORT_AUDIO
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
set
)
{
int
p
;
if
(
drv
->
ops
->
get_output_port
)
{
p
=
drv
->
ops
->
get_output_port
(
drv
);
if
(
p
&
set
)
return
0x6464
;
}
return
0
;
}
static
__inline__
int
OSS_IPORT_AUDIO
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
set
)
{
int
p
;
if
(
drv
->
ops
->
get_input_port
)
{
p
=
drv
->
ops
->
get_input_port
(
drv
);
if
(
p
&
set
)
return
0x6464
;
}
return
0
;
}
static
__inline__
void
OSS_TWIDDLE_PORT
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
ioctl
,
unsigned
int
port
,
unsigned
int
set
,
unsigned
int
value
)
{
if
(
ioctl
==
port
)
{
int
p
;
if
(
drv
->
ops
->
get_output_port
&&
drv
->
ops
->
set_output_port
)
{
p
=
drv
->
ops
->
get_output_port
(
drv
);
if
((
value
==
0
)
||
((
p
&
set
)
&&
(
OSS_LEFT
(
value
)
<
100
)))
drv
->
ops
->
set_output_port
(
drv
,
p
&
~
(
set
));
else
drv
->
ops
->
set_output_port
(
drv
,
p
|
set
);
}
}
}
static
__inline__
void
OSS_TWIDDLE_IPORT
(
struct
sparcaudio_driver
*
drv
,
unsigned
int
ioctl
,
unsigned
int
port
,
unsigned
int
set
,
unsigned
int
value
)
{
if
(
ioctl
==
port
)
{
int
p
;
if
(
drv
->
ops
->
get_input_port
&&
drv
->
ops
->
set_input_port
)
{
p
=
drv
->
ops
->
get_input_port
(
drv
);
if
((
value
==
0
)
||
((
p
&
set
)
&&
(
OSS_LEFT
(
value
)
<
100
)))
drv
->
ops
->
set_input_port
(
drv
,
p
&
~
(
set
));
else
drv
->
ops
->
set_input_port
(
drv
,
p
|
set
);
}
}
}
#endif
/* __KERNEL__ */
#endif
/* _AUDIOIO_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment