提交 b5d425c9 编写于 作者: A Adrian Bunk 提交者: Linus Torvalds

more scheduled OSS driver removal

This patch contains the scheduled removal of OSS drivers that:
- have ALSA drivers for the same hardware without known regressions and
- whose Kconfig options have been removed in 2.6.20.
Signed-off-by: NAdrian Bunk <bunk@stusta.de>
Acked-by: NJeff Garzik <jeff@garzik.org>
Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
上级 786d7e16
......@@ -119,13 +119,6 @@ Who: Adrian Bunk <bunk@stusta.de>
---------------------------
What: drivers depending on OSS_OBSOLETE_DRIVER
When: options in 2.6.20, code in 2.6.22
Why: OSS drivers with ALSA replacements
Who: Adrian Bunk <bunk@stusta.de>
---------------------------
What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports
(temporary transition config option provided until then)
The transition config option will also be removed at the same time.
......
......@@ -238,16 +238,9 @@ and is between 256 and 4096 characters. It is defined in the file
Disable PIN 1 of APIC timer
Can be useful to work around chipset bugs.
ad1816= [HW,OSS]
Format: <io>,<irq>,<dma>,<dma2>
See also Documentation/sound/oss/AD1816.
ad1848= [HW,OSS]
Format: <io>,<irq>,<dma>,<dma2>,<type>
adlib= [HW,OSS]
Format: <io>
advansys= [HW,SCSI]
See header of drivers/scsi/advansys.c.
......@@ -1206,9 +1199,6 @@ and is between 256 and 4096 characters. It is defined in the file
opl3= [HW,OSS]
Format: <io>
opl3sa2= [HW,OSS] Format:
<io>,<irq>,<dma>,<dma2>,<mss_io>,<mpu_io>,<ymode>,<loopback>[,<isapnp>,<multiple]
oprofile.timer= [HW]
Use timer interrupt instead of performance counters
......
Documentation for the AD1816(A) sound driver
============================================
Installation:
-------------
To get your AD1816(A) based sound card work, you'll have to enable support for
experimental code ("Prompt for development and/or incomplete code/drivers")
and isapnp ("Plug and Play support", "ISA Plug and Play support"). Enable
"Sound card support", "OSS modules support" and "Support for AD1816(A) based
cards (EXPERIMENTAL)" in the sound configuration menu, too. Now build, install
and reboot the new kernel as usual.
Features:
---------
List of features supported by this driver:
- full-duplex support
- supported audio formats: unsigned 8bit, signed 16bit little endian,
signed 16bit big endian, µ-law, A-law
- supported channels: mono and stereo
- supported recording sources: Master, CD, Line, Line1, Line2, Mic
- supports phat 3d stereo circuit (Line 3)
Supported cards:
----------------
The following cards are known to work with this driver:
- Terratec Base 1
- Terratec Base 64
- HP Kayak
- Acer FX-3D
- SY-1816
- Highscreen Sound-Boostar 32 Wave 3D
- Highscreen Sound-Boostar 16
- AVM Apex Pro card
- (Aztech SC-16 3D)
- (Newcom SC-16 3D)
- (Terratec EWS64S)
Cards listed in brackets are not supported reliable. If you have such a card
you should add the extra parameter:
options=1
when loading the ad1816 module via modprobe.
Troubleshooting:
----------------
First of all you should check, if the driver has been loaded
properly.
If loading of the driver succeeds, but playback/capture fails, check
if you used the correct values for irq, dma and dma2 when loading the module.
If one of them is wrong you usually get the following error message:
Nov 6 17:06:13 tek01 kernel: Sound: DMA (output) timed out - IRQ/DRQ config error?
If playback/capture is too fast or to slow, you should have a look at
the clock chip of your sound card. The AD1816 was designed for a 33MHz
oscillator, however most sound card manufacturer use slightly
different oscillators as they are cheaper than 33MHz oscillators. If
you have such a card you have to adjust the ad1816_clockfreq parameter
above. For example: For a card using a 32.875MHz oscillator use
ad1816_clockfreq=32875 instead of ad1816_clockfreq=33000.
Updates, bugfixes and bugreports:
--------------------------------
As the driver is still experimental and under development, you should
watch out for updates. Updates of the driver are available on the
Internet from one of my home pages:
http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html
or:
http://www.tu-darmstadt.de/~tek01/projects/linux.html
Bugreports, bugfixes and related questions should be sent via E-Mail to:
tek@rbg.informatik.tu-darmstadt.de
Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de>
Christoph Hellwig <hch@infradead.org>
Last modified: 2000/09/20
=======================================================
Documentation for the NeoMagic 256AV/256ZX sound driver
=======================================================
You're looking at version 1.1 of the driver. (Woohoo!) It has been
successfully tested against the following laptop models:
Sony Z505S/Z505SX/Z505DX/Z505RX
Sony F150, F160, F180, F250, F270, F280, PCG-F26
Dell Latitude CPi, CPt (various submodels)
There are a few caveats, which is why you should read the entirety of
this document first.
This driver was developed without any support or assistance from
NeoMagic. There is no warranty, expressed, implied, or otherwise. It
is free software in the public domain; feel free to use it, sell it,
give it to your best friends, even claim that you wrote it (but why?!)
but don't go whining to me, NeoMagic, Sony, Dell, or anyone else
when it blows up your computer.
Version 1.1 contains a change to try and detect non-AC97 versions of
the hardware, and not install itself appropriately. It should also
reinitialize the hardware on an APM resume event, assuming that APM
was configured into your kernel.
============
Installation
============
Enable the sound drivers, the OSS sound drivers, and then the NM256
driver. The NM256 driver *must* be configured as a module (it won't
give you any other choice).
Next, do the usual "make modules" and "make modules_install".
Finally, insmod the soundcore, sound and nm256 modules.
When the nm256 driver module is loaded, you should see a couple of
confirmation messages in the kernel logfile indicating that it found
the device (the device does *not* use any I/O ports or DMA channels).
Now try playing a wav file, futz with the CD-ROM if you have one, etc.
The NM256 is entirely a PCI-based device, and all the necessary
information is automatically obtained from the card. It can only be
configured as a module in a vain attempt to prevent people from
hurting themselves. It works correctly if it shares an IRQ with
another device (it normally shares IRQ 9 with the builtin eepro100
ethernet on the Sony Z505 laptops).
It does not run the card in any sort of compatibility mode. It will
not work on laptops that have the SB16-compatible, AD1848-compatible
or CS4232-compatible codec/mixer; you will want to use the appropriate
compatible OSS driver with these chipsets. I cannot provide any
assistance with machines using the SB16, AD1848 or CS4232 compatible
versions. (The driver now attempts to detect the mixer version, and
will refuse to load if it believes the hardware is not
AC97-compatible.)
The sound support is very basic, but it does include simultaneous
playback and record capability. The mixer support is also quite
simple, although this is in keeping with the rather limited
functionality of the chipset.
There is no hardware synthesizer available, as the Losedows OPL-3 and
MIDI support is done via hardware emulation.
Only three recording devices are available on the Sony: the
microphone, the CD-ROM input, and the volume device (which corresponds
to the stereo output). (Other devices may be available on other
models of laptops.) The Z505 series does not have a builtin CD-ROM,
so of course the CD-ROM input doesn't work. It does work on laptops
with a builtin CD-ROM drive.
The mixer device does not appear to have any tone controls, at least
on the Z505 series. The mixer module checks for tone controls in the
AC97 mixer, and will enable them if they are available.
==============
Known problems
==============
* There are known problems with PCMCIA cards and the eepro100 ethernet
driver on the Z505S/Z505SX/Z505DX. Keep reading.
* There are also potential problems with using a virtual X display, and
also problems loading the module after the X server has been started.
Keep reading.
* The volume control isn't anywhere near linear. Sorry. This will be
fixed eventually, when I get sufficiently annoyed with it. (I doubt
it will ever be fixed now, since I've never gotten sufficiently
annoyed with it and nobody else seems to care.)
* There are reports that the CD-ROM volume is very low. Since I do not
have a CD-ROM equipped laptop, I cannot test this (it's kinda hard to
do remotely).
* Only 8 fixed-rate speeds are supported. This is mainly a chipset
limitation. It may be possible to support other speeds in the future.
* There is no support for the telephone mixer/codec. There is support
for a phonein/phoneout device in the mixer driver; whether or not
it does anything is anyone's guess. (Reports on this would be
appreciated. You'll have to figure out how to get the phone to
go off-hook before it'll work, tho.)
* This driver was not written with any cooperation or support from
NeoMagic. If you have any questions about this, see their website
for their official stance on supporting open source drivers.
============
Video memory
============
The NeoMagic sound engine uses a portion of the display memory to hold
the sound buffer. (Crazy, eh?) The NeoMagic video BIOS sets up a
special pointer at the top of video RAM to indicate where the top of
the audio buffer should be placed.
At the present time XFree86 is apparently not aware of this. It will
thus write over either the pointer or the sound buffer with abandon.
(Accelerated-X seems to do a better job here.)
This implies a few things:
* Sometimes the NM256 driver has to guess at where the buffer
should be placed, especially if the module is loaded after the
X server is started. It's usually correct, but it will consistently
fail on the Sony F250.
* Virtual screens greater than 1024x768x16 under XFree86 are
problematic on laptops with only 2.5MB of screen RAM. This
includes all of the 256AV-equipped laptops. (Virtual displays
may or may not work on the 256ZX, which has at least 4MB of
video RAM.)
If you start having problems with random noise being output either
constantly (this is the usual symptom on the F250), or when windows
are moved around (this is the usual symptom when using a virtual
screen), the best fix is to
* Don't use a virtual frame buffer.
* Make sure you load the NM256 module before the X server is
started.
On the F250, it is possible to force the driver to load properly even
after the XFree86 server is started by doing:
insmod nm256 buffertop=0x25a800
This forces the audio buffers to the correct offset in screen RAM.
One user has reported a similar problem on the Sony F270, although
others apparently aren't seeing any problems. His suggested command
is
insmod nm256 buffertop=0x272800
=================
Official WWW site
=================
The official site for the NM256 driver is:
http://www.uglx.org/sony.html
You should always be able to get the latest version of the driver there,
and the driver will be supported for the foreseeable future.
==============
Z505RX and IDE
==============
There appears to be a problem with the IDE chipset on the Z505RX; one
of the symptoms is that sound playback periodically hangs (when the
disk is accessed). The user reporting the problem also reported that
enabling all of the IDE chipset workarounds in the kernel solved the
problem, tho obviously only one of them should be needed--if someone
can give me more details I would appreciate it.
==============================
Z505S/Z505SX on-board Ethernet
==============================
If you're using the on-board Ethernet Pro/100 ethernet support on the Z505
series, I strongly encourage you to download the latest eepro100 driver from
Donald Becker's site:
ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/test/eepro100.c
There was a reported problem on the Z505SX that if the ethernet
interface is disabled and reenabled while the sound driver is loaded,
the machine would lock up. I have included a workaround that is
working satisfactorily. However, you may occasionally see a message
about "Releasing interrupts, over 1000 bad interrupts" which indicates
that the workaround is doing its job.
==================================
PCMCIA and the Z505S/Z505SX/Z505DX
==================================
There is also a known problem with the Sony Z505S and Z505SX hanging
if a PCMCIA card is inserted while the ethernet driver is loaded, or
in some cases if the laptop is suspended. This is caused by tons of
spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges.
There is currently no fix for the problem that works in every case.
The only known workarounds are to disable the ethernet interface
before inserting or removing a PCMCIA card, or with some cards
disabling the PCMCIA card before ejecting it will also help the
problem with the laptop hanging when the card is ejected.
One user has reported that setting the tcic's cs_irq to some value
other than 9 (like 11) fixed the problem. This doesn't work on my
Z505S, however--changing the value causes the cardmgr to stop seeing
card insertions and removals, cards don't seem to work correctly, and
I still get hangs if a card is inserted when the kernel is booted.
Using the latest ethernet driver and pcmcia package allows me to
insert an Adaptec 1480A SlimScsi card without the laptop hanging,
although I still have to shut down the card before ejecting or
powering down the laptop. However, similar experiments with a DE-660
ethernet card still result in hangs when the card is inserted. I am
beginning to think that the interrupts are CardBus-related, since the
Adaptec card is a CardBus card, and the DE-660 is not; however, I
don't have any other CardBus cards to test with.
======
Thanks
======
First, I want to thank everyone (except NeoMagic of course) for their
generous support and encouragement. I'd like to list everyone's name
here that replied during the development phase, but the list is
amazingly long.
I will be rather unfair and single out a few people, however:
Justin Maurer, for being the first random net.person to try it,
and for letting me login to his Z505SX to get it working there
Edi Weitz for trying out several different versions, and giving
me a lot of useful feedback
Greg Rumple for letting me login remotely to get the driver
functional on the 256ZX, for his assistance on tracking
down all sorts of random stuff, and for trying out Accel-X
Zach Brown, for the initial AC97 mixer interface design
Jeff Garzik, for various helpful suggestions on the AC97
interface
"Mr. Bumpy" for feedback on the Z505RX
Bill Nottingham, for generous assistance in getting the mixer ID
code working
=================
Previous versions
=================
Versions prior to 0.3 (aka `noname') had problems with weird artifacts
in the output and failed to set the recording rate properly. These
problems have long since been fixed.
Versions prior to 0.5 had problems with clicks in the output when
anything other than 16-bit stereo sound was being played, and also had
periodic clicks when recording.
Version 0.7 first incorporated support for the NM256ZX chipset, which
is found on some Dell Latitude laptops (the CPt, and apparently
some CPi models as well). It also included the generic AC97
mixer module.
Version 0.75 renamed all the functions and files with slightly more
generic names.
Note that previous versions of this document claimed that recording was
8-bit only; it actually has been working for 16-bits all along.
Documentation for the OPL3-SA2, SA3, and SAx driver (opl3sa2.o)
---------------------------------------------------------------
Scott Murray, scott@spiteful.org
January 7, 2001
NOTE: All trade-marked terms mentioned below are properties of their
respective owners.
Supported Devices
-----------------
This driver is for PnP soundcards based on the following Yamaha audio
controller chipsets:
YMF711 aka OPL3-SA2
YMF715 and YMF719 aka OPL3-SA3
Up until recently (December 2000), I'd thought the 719 to be a
different chipset, the OPL3-SAx. After an email exhange with
Yamaha, however, it turns out that the 719 is just a re-badged
715, and the chipsets are identical. The chipset detection code
has been updated to reflect this.
Anyways, all of these chipsets implement the following devices:
OPL3 FM synthesizer
Soundblaster Pro
Microsoft/Windows Sound System
MPU401 MIDI interface
Note that this driver uses the MSS device, and to my knowledge these
chipsets enforce an either/or situation with the Soundblaster Pro
device and the MSS device. Since the MSS device has better
capabilities, I have implemented the driver to use it.
Mixer Channels
--------------
Older versions of this driver (pre-December 2000) had two mixers,
an OPL3-SA2 or SA3 mixer and a MSS mixer. The OPL3-SA[23] mixer
device contained a superset of mixer channels consisting of its own
channels and all of the MSS mixer channels. To simplify the driver
considerably, and to partition functionality better, the OPL3-SA[23]
mixer device now contains has its own specific mixer channels. They
are:
Volume - Hardware master volume control
Bass - SA3 only, now supports left and right channels
Treble - SA3 only, now supports left and right channels
Microphone - Hardware microphone input volume control
Digital1 - Yamaha 3D enhancement "Wide" mixer
All other mixer channels (e.g. "PCM", "CD", etc.) now have to be
controlled via the "MS Sound System (CS4231)" mixer. To facilitate
this, the mixer device creation order has been switched so that
the MSS mixer is created first. This allows accessing the majority
of the useful mixer channels even via single mixer-aware tools
such as "aumix".
Plug 'n Play
------------
In previous kernels (2.2.x), some configuration was required to
get the driver to talk to the card. Being the new millennium and
all, the 2.4.x kernels now support auto-configuration if ISA PnP
support is configured in. Theoretically, the driver even supports
having more than one card in this case.
With the addition of PnP support to the driver, two new parameters
have been added to control it:
isapnp - set to 0 to disable ISA PnP card detection
multiple - set to 0 to disable multiple PnP card detection
Optional Parameters
-------------------
Recent (December 2000) additions to the driver (based on a patch
provided by Peter Englmaier) are two new parameters:
ymode - Set Yamaha 3D enhancement mode:
0 = Desktop/Normal 5-12 cm speakers
1 = Notebook PC (1) 3 cm speakers
2 = Notebook PC (2) 1.5 cm speakers
3 = Hi-Fi 16-38 cm speakers
loopback - Set A/D input source. Useful for echo cancellation:
0 = Mic Right channel (default)
1 = Mono output loopback
The ymode parameter has been tested and does work. The loopback
parameter, however, is untested. Any feedback on its usefulness
would be appreciated.
Manual Configuration
--------------------
If for some reason you decide not to compile ISA PnP support into
your kernel, or disabled the driver's usage of it by setting the
isapnp parameter as discussed above, then you will need to do some
manual configuration. There are two ways of doing this. The most
common is to use the isapnptools package to initialize the card, and
use the kernel module form of the sound subsystem and sound drivers.
Alternatively, some BIOS's allow manual configuration of installed
PnP devices in a BIOS menu, which should allow using the non-modular
sound drivers, i.e. built into the kernel.
I personally use isapnp and modules, and do not have access to a PnP
BIOS machine to test. If you have such a beast, configuring the
driver to be built into the kernel should just work (thanks to work
done by David Luyer <luyer@ucs.uwa.edu.au>). You will still need
to specify settings, which can be done by adding:
opl3sa2=<io>,<irq>,<dma>,<dma2>,<mssio>,<mpuio>
to the kernel command line. For example:
opl3sa2=0x370,5,0,1,0x530,0x330
If you are instead using the isapnp tools (as most people have been
before Linux 2.4.x), follow the directions in their documentation to
produce a configuration file. Here is the relevant excerpt I used to
use for my SA3 card from my isapnp.conf:
(CONFIGURE YMH0800/-1 (LD 0
# NOTE: IO 0 is for the unused SoundBlaster part of the chipset.
(IO 0 (BASE 0x0220))
(IO 1 (BASE 0x0530))
(IO 2 (BASE 0x0388))
(IO 3 (BASE 0x0330))
(IO 4 (BASE 0x0370))
(INT 0 (IRQ 5 (MODE +E)))
(DMA 0 (CHANNEL 0))
(DMA 1 (CHANNEL 1))
Here, note that:
Port Acceptable Range Purpose
---- ---------------- -------
IO 0 0x0220 - 0x0280 SB base address, unused.
IO 1 0x0530 - 0x0F48 MSS base address
IO 2 0x0388 - 0x03F8 OPL3 base address
IO 3 0x0300 - 0x0334 MPU base address
IO 4 0x0100 - 0x0FFE card's own base address for its control I/O ports
The IRQ and DMA values can be any that are considered acceptable for a
MSS. Assuming you've got isapnp all happy, then you should be able to
do something like the following (which matches up with the isapnp
configuration above):
modprobe mpu401
modprobe ad1848
modprobe opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=5 dma=0 dma2=1
modprobe opl3 io=0x388
See the section "Automatic Module Loading" below for how to set up
/etc/modprobe.conf to automate this.
An important thing to remember that the opl3sa2 module's io argument is
for it's own control port, which handles the card's master mixer for
volume (on all cards), and bass and treble (on SA3 cards).
Troubleshooting
---------------
If all goes well and you see no error messages, you should be able to
start using the sound capabilities of your system. If you get an
error message while trying to insert the opl3sa2 module, then make
sure that the values of the various arguments match what you specified
in your isapnp configuration file, and that there is no conflict with
another device for an I/O port or interrupt. Checking the contents of
/proc/ioports and /proc/interrupts can be useful to see if you're
butting heads with another device.
If you still cannot get the module to load, look at the contents of
your system log file, usually /var/log/messages. If you see the
message "opl3sa2: Unknown Yamaha audio controller version", then you
have a different chipset version than I've encountered so far. Look
for all messages in the log file that start with "opl3sa2: " and see
if they provide any clues. If you do not see the chipset version
message, and none of the other messages present in the system log are
helpful, email me some details and I'll try my best to help.
Automatic Module Loading
------------------------
Lastly, if you're using modules and want to set up automatic module
loading with kmod, the kernel module loader, here is the section I
currently use in my modprobe.conf file:
# Sound
alias sound-slot-0 opl3sa2
options opl3sa2 io=0x370 mss_io=0x530 mpu_io=0x330 irq=7 dma=0 dma2=3
options opl3 io=0x388
That's all it currently takes to get an OPL3-SA3 card working on my
system. Once again, if you have any other problems, email me at the
address listed above.
Scott
Running sound cards on VIA chipsets
o There are problems with VIA chipsets and sound cards that appear to
lock the hardware solidly. Test programs under DOS have verified the
problem exists on at least some (but apparently not all) VIA boards
o VIA have so far failed to bother to answer support mail on the subject
so if you are a VIA engineer feeling aggrieved as you read this
document go chase your own people. If there is a workaround please
let us know so we can implement it.
Certain patterns of ISA DMA access used for most PC sound cards cause the
VIA chipsets to lock up. From the collected reports this appears to cover a
wide range of boards. Some also lock up with sound cards under Win* as well.
Linux implements a workaround providing your chipset is PCI and you compiled
with PCI Quirks enabled. If so you will see a message
"Activating ISA DMA bug workarounds"
during booting. If you have a VIA PCI chipset that hangs when you use the
sound and is not generating this message even with PCI quirks enabled
please report the information to the linux-kernel list (see REPORTING-BUGS).
If you are one of the tiny number of unfortunates with a 486 ISA/VLB VIA
chipset board you need to do the following to build a special kernel for
your board
edit linux/include/asm-i386/dma.h
change
#define isa_dma_bridge_buggy (0)
to
#define isa_dma_bridge_buggy (1)
and rebuild a kernel without PCI quirk support.
Other than this particular glitch the VIA [M]VP* chipsets appear to work
perfectly with Linux.
Documentation for the Cirrus Logic/Crystal SoundFusion cs46xx/cs4280 audio
controller chips (2001/05/11)
The cs46xx audio driver supports the DSP line of Cirrus controllers.
Specifically, the cs4610, cs4612, cs4614, cs4622, cs4624, cs4630 and the cs4280
products. This driver uses the generic ac97_codec driver for AC97 codec
support.
Features:
Full Duplex Playback/Capture supported from 8k-48k.
16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported.
APM/PM - 2.2.x PM is enabled and functional. APM can also
be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro
definition.
DMA playback buffer size is configurable from 16k (defaultorder=2) up to 2Meg
(defaultorder=11). DMA capture buffer size is fixed at a single 4k page as
two 2k fragments.
MMAP seems to work well with QuakeIII, and test XMMS plugin.
Myth2 works, but the polling logic is not fully correct, but is functional.
The 2.4.4-ac6 gameport code in the cs461x joystick driver has been tested
with a Microsoft Sidewinder joystick (cs461x.o and sidewinder.o). This
audio driver must be loaded prior to the joystick driver to enable the
DSP task image supporting the joystick device.
Limitations:
SPDIF is currently not supported.
Primary codec support only. No secondary codec support is implemented.
NOTES:
Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp,
and has been tested.
Module parameter hercules_egpio_disable set to 1, will force a 0 to EGPIODR
to disable the external amplifier.
VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control
the external amplifier for the "back" speakers, since we do not
support the secondary codec then this external amp is not
turned on. The primary codec external amplifier is supported but
note that the AC97 EAPD bit is inverted logic (amp_voyetra()).
DMA buffer size - there are issues with many of the Linux applications
concerning the optimal buffer size. Several applications request a
certain fragment size and number and then do not verify that the driver
has the ability to support the requested configuration.
SNDCTL_DSP_SETFRAGMENT ioctl is used to request a fragment size and
number of fragments. Some applications exit if an error is returned
on this particular ioctl. Therefore, in alignment with the other OSS audio
drivers, no error is returned when a SETFRAGs IOCTL is received, but the
values passed from the app are not used in any buffer calculation
(ossfragshift/ossmaxfrags are not used).
Use the "defaultorder=N" module parameter to change the buffer size if
you have an application that requires a specific number of fragments
or a specific buffer size (see below).
Debug Interface
---------------
There is an ioctl debug interface to allow runtime modification of the
debug print levels. This debug interface code can be disabled from the
compilation process with commenting the following define:
#define CSDEBUG_INTERFACE 1
There is also a debug print methodolgy to select printf statements from
different areas of the driver. A debug print level is also used to allow
additional printfs to be active. Comment out the following line in the
driver to disable compilation of the CS_DBGOUT print statements:
#define CSDEBUG 1
Please see the definitions for cs_debuglevel and cs_debugmask for additional
information on the debug levels and sections.
There is also a csdbg executable to allow runtime manipulation of these
parameters. for a copy email: twoller@crystal.cirrus.com
MODULE_PARMS definitions
------------------------
module_param(defaultorder, ulong, 0);
defaultorder=N
where N is a value from 1 to 12
The buffer order determines the size of the dma buffer for the driver.
under Linux, a smaller buffer allows more responsiveness from many of the
applications (e.g. games). A larger buffer allows some of the apps (esound)
to not underrun the dma buffer as easily. As default, use 32k (order=3)
rather than 64k as some of the games work more responsively.
(2^N) * PAGE_SIZE = allocated buffer size
module_param(cs_debuglevel, ulong, 0644);
module_param(cs_debugmask, ulong, 0644);
cs_debuglevel=N
cs_debugmask=0xMMMMMMMM
where N is a value from 0 (no debug printfs), to 9 (maximum)
0xMMMMMMMM is a debug mask corresponding to the CS_xxx bits (see driver source).
module_param(hercules_egpio_disable, ulong, 0);
hercules_egpio_disable=N
where N is a 0 (enable egpio), or a 1 (disable egpio support)
module_param(initdelay, ulong, 0);
initdelay=N
This value is used to determine the millescond delay during the initialization
code prior to powering up the PLL. On laptops this value can be used to
assist with errors on resume, mostly with IBM laptops. Basically, if the
system is booted under battery power then the mdelay()/udelay() functions fail to
properly delay the required time. Also, if the system is booted under AC power
and then the power removed, the mdelay()/udelay() functions will not delay properly.
module_param(powerdown, ulong, 0);
powerdown=N
where N is 0 (disable any powerdown of the internal blocks) or 1 (enable powerdown)
module_param(external_amp, bool, 0);
external_amp=1
if N is set to 1, then force enabling the EAPD support in the primary AC97 codec.
override the detection logic and force the external amp bit in the AC97 0x26 register
to be reset (0). EAPD should be 0 for powerup, and 1 for powerdown. The VTB Santa Cruz
card has inverted logic, so there is a special function for these cards.
module_param(thinkpad, bool, 0);
thinkpad=1
if N is set to 1, then force enabling the clkrun functionality.
Currently, when the part is being used, then clkrun is disabled for the entire system,
but re-enabled when the driver is released or there is no outstanding open count.
......@@ -194,13 +194,6 @@ M: jes@trained-monkey.org
L: linux-acenic@sunsite.dk
S: Maintained
ACI MIXER DRIVER
P: Robert Siemer
M: Robert.Siemer@gmx.de
L: linux-sound@vger.kernel.org
W: http://www.stud.uni-karlsruhe.de/~uh1b/
S: Maintained
IPS SCSI RAID DRIVER
P: Adaptec OEM Raid Solutions
M: aacraid@adaptec.com
......@@ -272,21 +265,6 @@ L: linux-acpi@vger.kernel.org
W: http://acpi.sourceforge.net/
S: Supported
AD1816 SOUND DRIVER
P: Thorsten Knabe
M: Thorsten Knabe <linux@thorsten-knabe.de>
W: http://linux.thorsten-knabe.de
S: Maintained
AD1889 SOUND DRIVER
P: Kyle McMartin
M: kyle@parisc-linux.org
P: Thibaut Varene
M: T-Bone@parisc-linux.org
W: http://wiki.parisc-linux.org/AD1889
L: parisc-linux@lists.parisc-linux.org
S: Maintained
ADM1025 HARDWARE MONITOR DRIVER
P: Jean Delvare
M: khali@linux-fr.org
......@@ -1404,13 +1382,6 @@ M: raisch@de.ibm.com
L: general@lists.openfabrics.org
S: Supported
EMU10K1 SOUND DRIVER
P: James Courtier-Dutton
M: James@superbug.demon.co.uk
L: emu10k1-devel@lists.sourceforge.net
W: http://sourceforge.net/projects/emu10k1/
S: Maintained
EMULEX LPFC FC SCSI DRIVER
P: James Smart
M: james.smart@emulex.com
......@@ -2730,12 +2701,6 @@ L: osst-users@lists.sourceforge.net
L: linux-scsi@vger.kernel.org
S: Maintained
OPL3-SA2, SA3, and SAx DRIVER
P: Zwane Mwaikambo
M: zwane@arm.linux.org.uk
L: linux-sound@vger.kernel.org
S: Maintained
OPROFILE
P: Philippe Elie
M: phil.el@wanadoo.fr
......
......@@ -18,20 +18,15 @@ obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_CS4232) += cs4232.o uart401.o
obj-$(CONFIG_SOUND_MSS) += ad1848.o
obj-$(CONFIG_SOUND_OPL3SA2) += opl3sa2.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o
obj-$(CONFIG_SOUND_MPU401) += mpu401.o
obj-$(CONFIG_SOUND_UART6850) += uart6850.o
obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o
obj-$(CONFIG_SOUND_YM3812) += opl3.o
obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
obj-$(CONFIG_SOUND_AD1816) += ad1816.o
obj-$(CONFIG_SOUND_AD1889) += ad1889.o ac97_codec.o
obj-$(CONFIG_SOUND_ACI_MIXER) += aci.o
obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o
ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
......@@ -40,24 +35,16 @@ endif
obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o
obj-$(CONFIG_SOUND_ICH) += i810_audio.o ac97_codec.o
obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o
obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o
obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o
obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_SOUND_BT878) += btaudio.o
obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o
ifeq ($(CONFIG_MIDI_EMU10K1),y)
obj-$(CONFIG_SOUND_EMU10K1) += sound.o
endif
obj-$(CONFIG_SOUND_EMU10K1) += emu10k1/
obj-$(CONFIG_DMASOUND) += dmasound/
# Declare multi-part drivers.
......
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include "ac97.h"
/* Flag for mono controls. */
#define MO 0
/* And for stereo. */
#define ST 1
/* Whether or not the bits in the channel are inverted. */
#define INV 1
#define NINV 0
static struct ac97_chn_desc {
int ac97_regnum;
int oss_channel;
int maxval;
int is_stereo;
int oss_mask;
int recordNum;
u16 regmask;
int is_inverted;
} mixerRegs[] = {
{ AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV },
{ AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV },
{ AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV },
{ AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV },
{ AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV },
{ AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV },
{ AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV },
{ AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV },
{ AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV },
{ AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV },
{ AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV },
{ AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV },
{ AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV },
{ -1, -1, 0xff, 0, 0, -1, 0x0000, 0 },
};
static struct ac97_chn_desc *
ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel)
{
int x;
for (x = 0; mixerRegs[x].oss_channel != -1; x++) {
if (mixerRegs[x].oss_channel == oss_channel)
return mixerRegs + x;
}
return NULL;
}
static inline int
ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn)
{
return (dev->last_written_mixer_values[chn->ac97_regnum / 2]
!= AC97_REG_UNSUPPORTED);
}
int
ac97_init (struct ac97_hwint *dev)
{
int x;
int reg0;
/* Clear out the arrays of cached values. */
for (x = 0; x < AC97_REG_CNT; x++)
dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN;
for (x = 0; x < SOUND_MIXER_NRDEVICES; x++)
dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN;
/* Clear the device masks. */
dev->mixer_devmask = 0;
dev->mixer_stereomask = 0;
dev->mixer_recmask = 0;
/* ??? Do a "standard reset" via register 0? */
/* Hardware-dependent reset. */
if (dev->reset_device (dev))
return -1;
/* Check the mixer device capabilities. */
reg0 = dev->read_reg (dev, AC97_RESET);
if (reg0 < 0)
return -1;
/* Check for support for treble/bass controls. */
if (! (reg0 & 4)) {
dev->last_written_mixer_values[AC97_MASTER_TONE / 2]
= AC97_REG_UNSUPPORTED;
}
/* ??? There may be other tests here? */
/* Fill in the device masks. */
for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
if (ac97_is_valid_channel (dev, mixerRegs + x)) {
dev->mixer_devmask |= mixerRegs[x].oss_mask;
if (mixerRegs[x].is_stereo)
dev->mixer_stereomask |= mixerRegs[x].oss_mask;
if (mixerRegs[x].recordNum != -1)
dev->mixer_recmask |= mixerRegs[x].oss_mask;
}
}
return 0;
}
/* Return the contents of register REG; use the cache if the value in it
is valid. Returns a negative error code on failure. */
static int
ac97_get_register (struct ac97_hwint *dev, u8 reg)
{
if (reg > 127 || (reg & 1))
return -EINVAL;
/* See if it's in the cache, or if it's just plain invalid. */
switch (dev->last_written_mixer_values[reg / 2]) {
case AC97_REG_UNSUPPORTED:
return -EINVAL;
break;
case AC97_REGVAL_UNKNOWN:
dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg);
break;
default:
break;
}
return dev->last_written_mixer_values[reg / 2];
}
/* Write VALUE to AC97 register REG, and cache its value in the last-written
cache. Returns a negative error code on failure, or 0 on success. */
int
ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value)
{
if (reg > 127 || (reg & 1))
return -EINVAL;
if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED)
return -EINVAL;
else {
int res = dev->write_reg (dev, reg, value);
if (res >= 0) {
dev->last_written_mixer_values[reg / 2] = value;
return 0;
}
else
return res;
}
}
/* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If
IS_STEREO is set, VALUE is a stereo value; the left channel value
is in the lower 8 bits, and the right channel value is in the upper
8 bits.
A negative error code is returned on failure, or the unsigned
scaled value on success. */
static int
ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv)
{
/* Muted? */
if (value & AC97_MUTE)
return 0;
if (is_stereo)
return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8)
| (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
else {
int i;
/* Inverted. */
if (inv)
value = maxval - value;
i = (value * 100 + (maxval / 2)) / maxval;
if (i > 100)
i = 100;
if (i < 0)
i = 0;
return i;
}
}
static int
ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv)
{
if (is_stereo)
return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8)
| (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
else {
int i = ((value & 255) * maxval + 50) / 100;
if (inv)
i = maxval - i;
if (i < 0)
i = 0;
if (i > maxval)
i = maxval;
return i;
}
}
static int
ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value)
{
int scaled_value;
struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
int result;
if (channel == NULL)
return -ENODEV;
if (! ac97_is_valid_channel (dev, channel))
return -ENODEV;
scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval,
channel->is_stereo,
channel->is_inverted);
if (scaled_value < 0)
return scaled_value;
if (channel->regmask != 0) {
int mv;
int oldval = ac97_get_register (dev, channel->ac97_regnum);
if (oldval < 0)
return oldval;
for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
scaled_value <<= 1;
scaled_value &= channel->regmask;
scaled_value |= (oldval & ~channel->regmask);
}
result = ac97_put_register (dev, channel->ac97_regnum, scaled_value);
if (result == 0)
dev->last_written_OSS_values[oss_channel] = oss_value;
return result;
}
static int
ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel)
{
struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel);
int regval;
if (channel == NULL)
return -ENODEV;
if (! ac97_is_valid_channel (dev, channel))
return -ENODEV;
regval = ac97_get_register (dev, channel->ac97_regnum);
if (regval < 0)
return regval;
if (channel->regmask != 0) {
int mv;
regval &= channel->regmask;
for (mv = channel->regmask; ! (mv & 1); mv >>= 1)
regval >>= 1;
}
return ac97_scale_to_oss_val (regval, channel->maxval,
channel->is_stereo,
channel->is_inverted);
}
static int
ac97_get_recmask (struct ac97_hwint *dev)
{
int recReg = ac97_get_register (dev, AC97_RECORD_SELECT);
if (recReg < 0)
return recReg;
else {
int x;
for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) {
if (mixerRegs[x].recordNum == (recReg & 7))
return mixerRegs[x].oss_mask;
}
return -ENODEV;
}
}
static int
ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask)
{
int x;
if (oss_recmask == 0)
oss_recmask = SOUND_MIXER_MIC;
for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) {
if ((mixerRegs[x].recordNum >= 0)
&& (oss_recmask & mixerRegs[x].oss_mask))
break;
}
if (mixerRegs[x].ac97_regnum < 0)
return -ENODEV;
else {
int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum;
int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval);
if (res == 0)
return ac97_get_recmask (dev);
else
return res;
}
}
/* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on
success, or a negative error code. */
int
ac97_set_values (struct ac97_hwint *dev,
struct ac97_mixer_value_list *value_list)
{
int x;
for (x = 0; value_list[x].oss_channel != -1; x++) {
int chnum = value_list[x].oss_channel;
struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum);
if (chent != NULL) {
u16 val;
int res;
if (chent->is_stereo)
val = (value_list[x].value.stereo.right << 8)
| value_list[x].value.stereo.left;
else {
/* We do this so the returned value looks OK in the
mixer app. It's not necessary otherwise. */
val = (value_list[x].value.mono << 8)
| value_list[x].value.mono;
}
res = ac97_set_mixer (dev, chnum, val);
if (res < 0)
return res;
}
else
return -ENODEV;
}
return 0;
}
int
ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user *arg)
{
int ret;
switch (cmd) {
case SOUND_MIXER_READ_RECSRC:
ret = ac97_get_recmask (dev);
break;
case SOUND_MIXER_WRITE_RECSRC:
{
if (get_user (ret, (int __user *) arg))
ret = -EFAULT;
else
ret = ac97_set_recmask (dev, ret);
}
break;
case SOUND_MIXER_READ_CAPS:
ret = SOUND_CAP_EXCL_INPUT;
break;
case SOUND_MIXER_READ_DEVMASK:
ret = dev->mixer_devmask;
break;
case SOUND_MIXER_READ_RECMASK:
ret = dev->mixer_recmask;
break;
case SOUND_MIXER_READ_STEREODEVS:
ret = dev->mixer_stereomask;
break;
default:
/* Read or write request. */
ret = -EINVAL;
if (_IOC_TYPE (cmd) == 'M') {
int dir = _SIOC_DIR (cmd);
int channel = _IOC_NR (cmd);
if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) {
ret = 0;
if (dir & _SIOC_WRITE) {
int val;
if (get_user (val, (int __user *) arg) == 0)
ret = ac97_set_mixer (dev, channel, val);
else
ret = -EFAULT;
}
if (ret >= 0 && (dir & _SIOC_READ)) {
if (dev->last_written_OSS_values[channel]
== AC97_REGVAL_UNKNOWN)
dev->last_written_OSS_values[channel]
= ac97_get_mixer_scaled (dev, channel);
ret = dev->last_written_OSS_values[channel];
}
}
}
break;
}
if (ret < 0)
return ret;
else
return put_user(ret, (int __user *) arg);
}
EXPORT_SYMBOL(ac97_init);
EXPORT_SYMBOL(ac97_set_values);
EXPORT_SYMBOL(ac97_put_register);
EXPORT_SYMBOL(ac97_mixer_ioctl);
MODULE_LICENSE("GPL");
/*
* Local variables:
* c-basic-offset: 4
* End:
*/
/*
* ac97.h
*
* definitions for the AC97, Intel's Audio Codec 97 Spec
* also includes support for a generic AC97 interface
*/
#ifndef _AC97_H_
#define _AC97_H_
#include "sound_config.h"
#include "sound_calls.h"
#define AC97_RESET 0x0000 //
#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out
#define AC97_HEADPHONE_VOL 0x0004 //
#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output
#define AC97_MASTER_TONE 0x0008 //
#define AC97_PCBEEP_VOL 0x000a // none
#define AC97_PHONE_VOL 0x000c // TAD Input (mono)
#define AC97_MIC_VOL 0x000e // MIC Input (mono)
#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo)
#define AC97_CD_VOL 0x0012 // CD Input (stereo)
#define AC97_VIDEO_VOL 0x0014 // none
#define AC97_AUX_VOL 0x0016 // Aux Input (stereo)
#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo)
#define AC97_RECORD_SELECT 0x001a //
#define AC97_RECORD_GAIN 0x001c
#define AC97_RECORD_GAIN_MIC 0x001e
#define AC97_GENERAL_PURPOSE 0x0020
#define AC97_3D_CONTROL 0x0022
#define AC97_MODEM_RATE 0x0024
#define AC97_POWER_CONTROL 0x0026
/* registers 0x0028 - 0x0058 are reserved */
/* AC'97 2.0 */
#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */
#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */
#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */
#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */
#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */
#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR DAC Rate */
#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */
#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */
#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */
#define AC97_RESERVED_3A 0x003A /* Reserved */
/* range 0x3c-0x58 - MODEM */
/* registers 0x005a - 0x007a are vendor reserved */
#define AC97_VENDOR_ID1 0x007c
#define AC97_VENDOR_ID2 0x007e
/* volume control bit defines */
#define AC97_MUTE 0x8000
#define AC97_MICBOOST 0x0040
#define AC97_LEFTVOL 0x3f00
#define AC97_RIGHTVOL 0x003f
/* record mux defines */
#define AC97_RECMUX_MIC 0x0000
#define AC97_RECMUX_CD 0x0101
#define AC97_RECMUX_VIDEO 0x0202 /* not used */
#define AC97_RECMUX_AUX 0x0303
#define AC97_RECMUX_LINE 0x0404
#define AC97_RECMUX_STEREO_MIX 0x0505
#define AC97_RECMUX_MONO_MIX 0x0606
#define AC97_RECMUX_PHONE 0x0707
/* general purpose register bit defines */
#define AC97_GP_LPBK 0x0080 /* Loopback mode */
#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */
#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */
#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */
#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */
#define AC97_GP_LD 0x1000 /* Loudness 1=on */
#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */
#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */
#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */
/* powerdown control and status bit defines */
/* status */
#define AC97_PWR_MDM 0x0010 /* Modem section ready */
#define AC97_PWR_REF 0x0008 /* Vref nominal */
#define AC97_PWR_ANL 0x0004 /* Analog section ready */
#define AC97_PWR_DAC 0x0002 /* DAC section ready */
#define AC97_PWR_ADC 0x0001 /* ADC section ready */
/* control */
#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */
#define AC97_PWR_PR1 0x0200 /* DAC powerdown */
#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */
#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */
#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */
#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */
#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */
#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */
/* useful power states */
#define AC97_PWR_D0 0x0000 /* everything on */
#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4
#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4
#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */
/* Total number of defined registers. */
#define AC97_REG_CNT 64
/* Generic AC97 mixer interface. */
/* Structure describing access to the hardware. */
struct ac97_hwint
{
/* Perform any hardware-specific reset and initialization. Returns
0 on success, or a negative error code. */
int (*reset_device) (struct ac97_hwint *dev);
/* Returns the contents of the specified register REG. The caller
should check to see if the desired contents are available in
the cache first, if applicable. Returns a positive unsigned value
representing the contents of the register, or a negative error
code. */
int (*read_reg) (struct ac97_hwint *dev, u8 reg);
/* Writes VALUE to register REG. Returns 0 on success, or a
negative error code. */
int (*write_reg) (struct ac97_hwint *dev, u8 reg, u16 value);
/* Hardware-specific information. */
void *driver_private;
/* Three OSS masks. */
int mixer_devmask;
int mixer_stereomask;
int mixer_recmask;
/* The mixer cache. The indices correspond to the AC97 hardware register
number / 2, since the register numbers are always an even number.
Unknown values are set to -1; unsupported registers contain a
-2. */
int last_written_mixer_values[AC97_REG_CNT];
/* A cache of values written via OSS; we need these so we can return
the values originally written by the user.
Why the original user values? Because the real-world hardware
has less precision, and some existing applications assume that
they will get back the exact value that they wrote (aumix).
A -1 value indicates that no value has been written to this mixer
channel via OSS. */
int last_written_OSS_values[SOUND_MIXER_NRDEVICES];
};
/* Values stored in the register cache. */
#define AC97_REGVAL_UNKNOWN -1
#define AC97_REG_UNSUPPORTED -2
struct ac97_mixer_value_list
{
/* Mixer channel to set. List is terminated by a value of -1. */
int oss_channel;
/* The scaled value to set it to; values generally range from 0-100. */
union {
struct {
u8 left, right;
} stereo;
u8 mono;
} value;
};
/* Initialize the ac97 mixer by resetting it. */
extern int ac97_init (struct ac97_hwint *dev);
/* Sets the mixer DEV to the values in VALUE_LIST. Returns 0 on success,
or a negative error code. */
extern int ac97_set_values (struct ac97_hwint *dev,
struct ac97_mixer_value_list *value_list);
/* Writes the specified VALUE to the AC97 register REG in the mixer.
Takes care of setting the last-written cache as well. */
extern int ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value);
/* Default ioctl. */
extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd,
void __user * arg);
#endif
/*
* Local variables:
* c-basic-offset: 4
* End:
*/
/*
* Audio Command Interface (ACI) driver (sound/aci.c)
*
* ACI is a protocol used to communicate with the microcontroller on
* some sound cards produced by miro, e.g. the miroSOUND PCM12 and
* PCM20. The ACI has been developed for miro by Norberto Pellicci
* <pellicci@home.com>. Special thanks to both him and miro for
* providing the ACI specification.
*
* The main function of the ACI is to control the mixer and to get a
* product identification. On the PCM20, ACI also controls the radio
* tuner on this card, this is supported in the Video for Linux
* miropcm20 driver.
* -
* This is a fullfeatured implementation. Unsupported features
* are bugs... (:
*
* It is not longer necessary to load the mad16 module first. The
* user is currently responsible to set the mad16 mixer correctly.
*
* To toggle the solo mode for full duplex operation just use the OSS
* record switch for the pcm ('wave') controller. Robert
* -
*
* Revision history:
*
* 1995-11-10 Markus Kuhn <mskuhn@cip.informatik.uni-erlangen.de>
* First version written.
* 1995-12-31 Markus Kuhn
* Second revision, general code cleanup.
* 1996-05-16 Hannu Savolainen
* Integrated with other parts of the driver.
* 1996-05-28 Markus Kuhn
* Initialize CS4231A mixer, make ACI first mixer,
* use new private mixer API for solo mode.
* 1998-08-18 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
* Small modification to export ACI functions and
* complete modularisation.
* 2000-06-20 Robert Siemer <Robert.Siemer@gmx.de>
* Don't initialize the CS4231A mixer anymore, so the code is
* working again, and other small changes to fit in todays
* kernels.
* 2000-08-26 Robert Siemer
* Clean up and rewrite for 2.4.x. Maybe it's SMP safe now... (:
* ioctl bugfix, and integration of solo-mode into OSS-API,
* added (OSS-limited) equalizer support, return value bugfix,
* changed param aci_reset to reset, new params: ide, wss.
* 2001-04-20 Robert Siemer
* even more cleanups...
* 2001-10-08 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* Get rid of check_region, .bss optimizations, use set_current_state
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "sound_config.h"
int aci_port; /* as determined by bit 4 in the OPTi 929 MC4 register */
static int aci_idcode[2]; /* manufacturer and product ID */
int aci_version; /* ACI firmware version */
EXPORT_SYMBOL(aci_port);
EXPORT_SYMBOL(aci_version);
#include "aci.h"
static int aci_solo; /* status bit of the card that can't be *
* checked with ACI versions prior to 0xb0 */
static int aci_amp; /* status bit for power-amp/line-out level
but I have no docs about what is what... */
static int aci_micpreamp=3; /* microphone preamp-level that can't be *
* checked with ACI versions prior to 0xb0 */
static int mixer_device;
static struct mutex aci_mutex;
#ifdef MODULE
static int reset;
module_param(reset, bool, 0);
MODULE_PARM_DESC(reset,"When set to 1, reset aci mixer.");
#else
static int reset = 1;
#endif
static int ide=-1;
module_param(ide, int, 0);
MODULE_PARM_DESC(ide,"1 enable, 0 disable ide-port - untested"
" default: do nothing");
static int wss=-1;
module_param(wss, int, 0);
MODULE_PARM_DESC(wss,"change between ACI/WSS-mixer; use 0 and 1 - untested"
" default: do nothing; for PCM1-pro only");
#ifdef DEBUG
static void print_bits(unsigned char c)
{
int j;
printk(KERN_DEBUG "aci: ");
for (j=7; j>=0; j--) {
printk("%d", (c >> j) & 0x1);
}
printk("\n");
}
#endif
/*
* This busy wait code normally requires less than 15 loops and
* practically always less than 100 loops on my i486/DX2 66 MHz.
*
* Warning: Waiting on the general status flag after reseting the MUTE
* function can take a VERY long time, because the PCM12 does some kind
* of fade-in effect. For this reason, access to the MUTE function has
* not been implemented at all.
*
* - The OSS interface has no mute option. It takes about 3 seconds to
* fade-in on my PCM20. busy_wait() handles it great now... Robert
*/
static int busy_wait(void)
{
#define MINTIME 500
long timeout;
unsigned char byte;
for (timeout = 1; timeout <= MINTIME+30; timeout++) {
if (((byte=inb(BUSY_REGISTER)) & 1) == 0) {
if (timeout >= MINTIME)
printk(KERN_DEBUG "aci: Got READYFLAG in round %ld.\n", timeout-MINTIME);
return byte;
}
if (timeout >= MINTIME) {
long out=10*HZ;
switch (timeout-MINTIME) {
case 0 ... 9:
out /= 10;
case 10 ... 19:
out /= 10;
case 20 ... 30:
out /= 10;
default:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(out);
break;
}
}
}
printk(KERN_WARNING "aci: busy_wait() time out.\n");
return -EBUSY;
}
/* The four ACI command types are fucked up. [-:
* implied is: 1w - special case for INIT
* write is: 2w1r
* read is: x(1w1r) where x is 1 or 2 (1 CHECK_SIG, 1 CHECK_STER,
* 1 VERSION, 2 IDCODE)
* the command is only in the first write, rest is protocol overhead
*
* indexed is technically a write and used for STATUS
* and the special case for TUNE is: 3w1r
*
* Here the new general sheme: TUNE --> aci_rw_cmd(x, y, z)
* indexed and write --> aci_rw_cmd(x, y, -1)
* implied and read (x=1) --> aci_rw_cmd(x, -1, -1)
*
* Read (x>=2) is not implemented (only used during initialization).
* Use aci_idcode[2] and aci_version... Robert
*/
/* Some notes for error detection: theoretically it is possible.
* But it doubles the I/O-traffic from ww(r) to wwwrw(r) in the normal
* case and doesn't seem to be designed for that... Robert
*/
static inline int aci_rawwrite(unsigned char byte)
{
if (busy_wait() >= 0) {
#ifdef DEBUG
printk(KERN_DEBUG "aci_rawwrite(%d)\n", byte);
#endif
outb(byte, COMMAND_REGISTER);
return 0;
} else
return -EBUSY;
}
static inline int aci_rawread(void)
{
unsigned char byte;
if (busy_wait() >= 0) {
byte=inb(STATUS_REGISTER);
#ifdef DEBUG
printk(KERN_DEBUG "%d = aci_rawread()\n", byte);
#endif
return byte;
} else
return -EBUSY;
}
int aci_rw_cmd(int write1, int write2, int write3)
{
int write[] = {write1, write2, write3};
int read = -EINTR, i;
if (mutex_lock_interruptible(&aci_mutex))
goto out;
for (i=0; i<3; i++) {
if (write[i]< 0 || write[i] > 255)
break;
else {
read = aci_rawwrite(write[i]);
if (read < 0)
goto out_up;
}
}
read = aci_rawread();
out_up: mutex_unlock(&aci_mutex);
out: return read;
}
EXPORT_SYMBOL(aci_rw_cmd);
static int setvolume(int __user *arg,
unsigned char left_index, unsigned char right_index)
{
int vol, ret, uservol, buf;
__get_user(uservol, arg);
/* left channel */
vol = uservol & 0xff;
if (vol > 100)
vol = 100;
vol = SCALE(100, 0x20, vol);
if ((buf=aci_write_cmd(left_index, 0x20 - vol))<0)
return buf;
ret = SCALE(0x20, 100, vol);
/* right channel */
vol = (uservol >> 8) & 0xff;
if (vol > 100)
vol = 100;
vol = SCALE(100, 0x20, vol);
if ((buf=aci_write_cmd(right_index, 0x20 - vol))<0)
return buf;
ret |= SCALE(0x20, 100, vol) << 8;
__put_user(ret, arg);
return 0;
}
static int getvolume(int __user *arg,
unsigned char left_index, unsigned char right_index)
{
int vol;
int buf;
/* left channel */
if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
return buf;
vol = SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0);
/* right channel */
if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
return buf;
vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
__put_user(vol, arg);
return 0;
}
/* The equalizer is somewhat strange on the ACI. From -12dB to +12dB
* write: 0xff..down.to..0x80==0x00..up.to..0x7f
*/
static inline unsigned int eq_oss2aci(unsigned int vol)
{
int boost=0;
unsigned int ret;
if (vol > 100)
vol = 100;
if (vol > 50) {
vol -= 51;
boost=1;
}
if (boost)
ret=SCALE(49, 0x7e, vol)+1;
else
ret=0xff - SCALE(50, 0x7f, vol);
return ret;
}
static inline unsigned int eq_aci2oss(unsigned int vol)
{
if (vol < 0x80)
return SCALE(0x7f, 50, vol) + 50;
else
return SCALE(0x7f, 50, 0xff-vol);
}
static int setequalizer(int __user *arg,
unsigned char left_index, unsigned char right_index)
{
int buf;
unsigned int vol;
__get_user(vol, arg);
/* left channel */
if ((buf=aci_write_cmd(left_index, eq_oss2aci(vol & 0xff)))<0)
return buf;
/* right channel */
if ((buf=aci_write_cmd(right_index, eq_oss2aci((vol>>8) & 0xff)))<0)
return buf;
/* the ACI equalizer is more precise */
return 0;
}
static int getequalizer(int __user *arg,
unsigned char left_index, unsigned char right_index)
{
int buf;
unsigned int vol;
/* left channel */
if ((buf=aci_indexed_cmd(ACI_STATUS, left_index))<0)
return buf;
vol = eq_aci2oss(buf);
/* right channel */
if ((buf=aci_indexed_cmd(ACI_STATUS, right_index))<0)
return buf;
vol |= eq_aci2oss(buf) << 8;
__put_user(vol, arg);
return 0;
}
static int aci_mixer_ioctl (int dev, unsigned int cmd, void __user * arg)
{
int vol, buf;
int __user *p = arg;
switch (cmd) {
case SOUND_MIXER_WRITE_VOLUME:
return setvolume(p, 0x01, 0x00);
case SOUND_MIXER_WRITE_CD:
return setvolume(p, 0x3c, 0x34);
case SOUND_MIXER_WRITE_MIC:
return setvolume(p, 0x38, 0x30);
case SOUND_MIXER_WRITE_LINE:
return setvolume(p, 0x39, 0x31);
case SOUND_MIXER_WRITE_SYNTH:
return setvolume(p, 0x3b, 0x33);
case SOUND_MIXER_WRITE_PCM:
return setvolume(p, 0x3a, 0x32);
case MIXER_WRITE(SOUND_MIXER_RADIO): /* fall through */
case SOUND_MIXER_WRITE_LINE1: /* AUX1 or radio */
return setvolume(p, 0x3d, 0x35);
case SOUND_MIXER_WRITE_LINE2: /* AUX2 */
return setvolume(p, 0x3e, 0x36);
case SOUND_MIXER_WRITE_BASS: /* set band one and two */
if (aci_idcode[1]=='C') {
if ((buf=setequalizer(p, 0x48, 0x40)) ||
(buf=setequalizer(p, 0x49, 0x41)));
return buf;
}
break;
case SOUND_MIXER_WRITE_TREBLE: /* set band six and seven */
if (aci_idcode[1]=='C') {
if ((buf=setequalizer(p, 0x4d, 0x45)) ||
(buf=setequalizer(p, 0x4e, 0x46)));
return buf;
}
break;
case SOUND_MIXER_WRITE_IGAIN: /* MIC pre-amp */
if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
__get_user(vol, p);
vol = vol & 0xff;
if (vol > 100)
vol = 100;
vol = SCALE(100, 3, vol);
if ((buf=aci_write_cmd(ACI_WRITE_IGAIN, vol))<0)
return buf;
aci_micpreamp = vol;
vol = SCALE(3, 100, vol);
vol |= (vol << 8);
__put_user(vol, p);
return 0;
}
break;
case SOUND_MIXER_WRITE_OGAIN: /* Power-amp/line-out level */
if (aci_idcode[1]=='A' || aci_idcode[1]=='B') {
__get_user(buf, p);
buf = buf & 0xff;
if (buf > 50)
vol = 1;
else
vol = 0;
if ((buf=aci_write_cmd(ACI_SET_POWERAMP, vol))<0)
return buf;
aci_amp = vol;
if (aci_amp)
buf = (100 || 100<<8);
else
buf = 0;
__put_user(buf, p);
return 0;
}
break;
case SOUND_MIXER_WRITE_RECSRC:
/* handle solo mode control */
__get_user(buf, p);
/* unset solo when RECSRC for PCM is requested */
if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
vol = !(buf & SOUND_MASK_PCM);
if ((buf=aci_write_cmd(ACI_SET_SOLOMODE, vol))<0)
return buf;
aci_solo = vol;
}
buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
SOUND_MASK_SYNTH| SOUND_MASK_LINE2);
if (aci_idcode[1] == 'C') /* PCM20 radio */
buf |= SOUND_MASK_RADIO;
else
buf |= SOUND_MASK_LINE1;
if (!aci_solo)
buf |= SOUND_MASK_PCM;
__put_user(buf, p);
return 0;
case SOUND_MIXER_READ_DEVMASK:
buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD |
SOUND_MASK_MIC | SOUND_MASK_LINE |
SOUND_MASK_SYNTH | SOUND_MASK_PCM |
SOUND_MASK_LINE2);
switch (aci_idcode[1]) {
case 'C': /* PCM20 radio */
buf |= (SOUND_MASK_RADIO | SOUND_MASK_IGAIN |
SOUND_MASK_BASS | SOUND_MASK_TREBLE);
break;
case 'B': /* PCM12 */
buf |= (SOUND_MASK_LINE1 | SOUND_MASK_IGAIN |
SOUND_MASK_OGAIN);
break;
case 'A': /* PCM1-pro */
buf |= (SOUND_MASK_LINE1 | SOUND_MASK_OGAIN);
break;
default:
buf |= SOUND_MASK_LINE1;
}
__put_user(buf, p);
return 0;
case SOUND_MIXER_READ_STEREODEVS:
buf = (SOUND_MASK_VOLUME | SOUND_MASK_CD |
SOUND_MASK_MIC | SOUND_MASK_LINE |
SOUND_MASK_SYNTH | SOUND_MASK_PCM |
SOUND_MASK_LINE2);
switch (aci_idcode[1]) {
case 'C': /* PCM20 radio */
buf |= (SOUND_MASK_RADIO |
SOUND_MASK_BASS | SOUND_MASK_TREBLE);
break;
default:
buf |= SOUND_MASK_LINE1;
}
__put_user(buf, p);
return 0;
case SOUND_MIXER_READ_RECMASK:
buf = (SOUND_MASK_CD| SOUND_MASK_MIC| SOUND_MASK_LINE|
SOUND_MASK_SYNTH| SOUND_MASK_LINE2| SOUND_MASK_PCM);
if (aci_idcode[1] == 'C') /* PCM20 radio */
buf |= SOUND_MASK_RADIO;
else
buf |= SOUND_MASK_LINE1;
__put_user(buf, p);
return 0;
case SOUND_MIXER_READ_RECSRC:
buf = (SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE |
SOUND_MASK_SYNTH | SOUND_MASK_LINE2);
/* do we need aci_solo or can I get it from the ACI? */
switch (aci_idcode[1]) {
case 'B': /* PCM12 */
case 'C': /* PCM20 radio */
if (aci_version >= 0xb0) {
if ((vol=aci_rw_cmd(ACI_STATUS,
ACI_S_GENERAL, -1))<0)
return vol;
if (vol & 0x20)
buf |= SOUND_MASK_PCM;
}
else
if (!aci_solo)
buf |= SOUND_MASK_PCM;
break;
default:
buf |= SOUND_MASK_PCM;
}
if (aci_idcode[1] == 'C') /* PCM20 radio */
buf |= SOUND_MASK_RADIO;
else
buf |= SOUND_MASK_LINE1;
__put_user(buf, p);
return 0;
case SOUND_MIXER_READ_CAPS:
__put_user(0, p);
return 0;
case SOUND_MIXER_READ_VOLUME:
return getvolume(p, 0x04, 0x03);
case SOUND_MIXER_READ_CD:
return getvolume(p, 0x0a, 0x09);
case SOUND_MIXER_READ_MIC:
return getvolume(p, 0x06, 0x05);
case SOUND_MIXER_READ_LINE:
return getvolume(p, 0x08, 0x07);
case SOUND_MIXER_READ_SYNTH:
return getvolume(p, 0x0c, 0x0b);
case SOUND_MIXER_READ_PCM:
return getvolume(p, 0x0e, 0x0d);
case MIXER_READ(SOUND_MIXER_RADIO): /* fall through */
case SOUND_MIXER_READ_LINE1: /* AUX1 */
return getvolume(p, 0x11, 0x10);
case SOUND_MIXER_READ_LINE2: /* AUX2 */
return getvolume(p, 0x13, 0x12);
case SOUND_MIXER_READ_BASS: /* get band one */
if (aci_idcode[1]=='C') {
return getequalizer(p, 0x23, 0x22);
}
break;
case SOUND_MIXER_READ_TREBLE: /* get band seven */
if (aci_idcode[1]=='C') {
return getequalizer(p, 0x2f, 0x2e);
}
break;
case SOUND_MIXER_READ_IGAIN: /* MIC pre-amp */
if (aci_idcode[1]=='B' || aci_idcode[1]=='C') {
/* aci_micpreamp or ACI? */
if (aci_version >= 0xb0) {
if ((buf=aci_indexed_cmd(ACI_STATUS,
ACI_S_READ_IGAIN))<0)
return buf;
}
else
buf=aci_micpreamp;
vol = SCALE(3, 100, buf <= 3 ? buf : 3);
vol |= vol << 8;
__put_user(vol, p);
return 0;
}
break;
case SOUND_MIXER_READ_OGAIN:
if (aci_amp)
buf = (100 || 100<<8);
else
buf = 0;
__put_user(buf, p);
return 0;
}
return -EINVAL;
}
static struct mixer_operations aci_mixer_operations =
{
.owner = THIS_MODULE,
.id = "ACI",
.ioctl = aci_mixer_ioctl
};
/*
* There is also an internal mixer in the codec (CS4231A or AD1845),
* that deserves no purpose in an ACI based system which uses an
* external ACI controlled stereo mixer. Make sure that this codec
* mixer has the AUX1 input selected as the recording source, that the
* input gain is set near maximum and that the other channels going
* from the inputs to the codec output are muted.
*/
static int __init attach_aci(void)
{
char *boardname;
int i, rc = -EBUSY;
mutex_init(&aci_mutex);
outb(0xE3, 0xf8f); /* Write MAD16 password */
aci_port = (inb(0xf90) & 0x10) ?
0x344: 0x354; /* Get aci_port from MC4_PORT */
if (!request_region(aci_port, 3, "sound mixer (ACI)")) {
printk(KERN_NOTICE
"aci: I/O area 0x%03x-0x%03x already used.\n",
aci_port, aci_port+2);
goto out;
}
/* force ACI into a known state */
rc = -EFAULT;
for (i=0; i<3; i++)
if (aci_rw_cmd(ACI_ERROR_OP, -1, -1)<0)
goto out_release_region;
/* official this is one aci read call: */
rc = -EFAULT;
if ((aci_idcode[0]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0 ||
(aci_idcode[1]=aci_rw_cmd(ACI_READ_IDCODE, -1, -1))<0) {
printk(KERN_ERR "aci: Failed to read idcode on 0x%03x.\n",
aci_port);
goto out_release_region;
}
if ((aci_version=aci_rw_cmd(ACI_READ_VERSION, -1, -1))<0) {
printk(KERN_ERR "aci: Failed to read version on 0x%03x.\n",
aci_port);
goto out_release_region;
}
if (aci_idcode[0] == 'm') {
/* It looks like a miro sound card. */
switch (aci_idcode[1]) {
case 'A':
boardname = "PCM1 pro / early PCM12";
break;
case 'B':
boardname = "PCM12";
break;
case 'C':
boardname = "PCM20 radio";
break;
default:
boardname = "unknown miro";
}
} else {
printk(KERN_WARNING "aci: Warning: unsupported card! - "
"no hardware, no specs...\n");
boardname = "unknown Cardinal Technologies";
}
printk(KERN_INFO "<ACI 0x%02x, id %02x/%02x \"%c/%c\", (%s)> at 0x%03x\n",
aci_version,
aci_idcode[0], aci_idcode[1],
aci_idcode[0], aci_idcode[1],
boardname, aci_port);
rc = -EBUSY;
if (reset) {
/* first write()s after reset fail with my PCM20 */
if (aci_rw_cmd(ACI_INIT, -1, -1)<0 ||
aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0 ||
aci_rw_cmd(ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP)<0)
goto out_release_region;
}
/* the PCM20 is muted after reset (and reboot) */
if (aci_rw_cmd(ACI_SET_MUTE, 0x00, -1)<0)
goto out_release_region;
if (ide>=0)
if (aci_rw_cmd(ACI_SET_IDE, !ide, -1)<0)
goto out_release_region;
if (wss>=0 && aci_idcode[1]=='A')
if (aci_rw_cmd(ACI_SET_WSS, !!wss, -1)<0)
goto out_release_region;
mixer_device = sound_install_mixer(MIXER_DRIVER_VERSION, boardname,
&aci_mixer_operations,
sizeof(aci_mixer_operations), NULL);
rc = 0;
if (mixer_device < 0) {
printk(KERN_ERR "aci: Failed to install mixer.\n");
rc = mixer_device;
goto out_release_region;
} /* else Maybe initialize the CS4231A mixer here... */
out: return rc;
out_release_region:
release_region(aci_port, 3);
goto out;
}
static void __exit unload_aci(void)
{
sound_unload_mixerdev(mixer_device);
release_region(aci_port, 3);
}
module_init(attach_aci);
module_exit(unload_aci);
MODULE_LICENSE("GPL");
#ifndef _ACI_H_
#define _ACI_H_
extern int aci_port;
extern int aci_version; /* ACI firmware version */
extern int aci_rw_cmd(int write1, int write2, int write3);
#define aci_indexed_cmd(a, b) aci_rw_cmd(a, b, -1)
#define aci_write_cmd(a, b) aci_rw_cmd(a, b, -1)
#define aci_read_cmd(a) aci_rw_cmd(a,-1, -1)
#define COMMAND_REGISTER (aci_port) /* write register */
#define STATUS_REGISTER (aci_port + 1) /* read register */
#define BUSY_REGISTER (aci_port + 2) /* also used for rds */
#define RDS_REGISTER BUSY_REGISTER
#define ACI_SET_MUTE 0x0d
#define ACI_SET_POWERAMP 0x0f
#define ACI_SET_TUNERMUTE 0xa3
#define ACI_SET_TUNERMONO 0xa4
#define ACI_SET_IDE 0xd0
#define ACI_SET_WSS 0xd1
#define ACI_SET_SOLOMODE 0xd2
#define ACI_WRITE_IGAIN 0x03
#define ACI_WRITE_TUNE 0xa7
#define ACI_READ_TUNERSTEREO 0xa8
#define ACI_READ_TUNERSTATION 0xa9
#define ACI_READ_VERSION 0xf1
#define ACI_READ_IDCODE 0xf2
#define ACI_INIT 0xff
#define ACI_STATUS 0xf0
#define ACI_S_GENERAL 0x00
#define ACI_S_READ_IGAIN 0x21
#define ACI_ERROR_OP 0xdf
/*
* The following macro SCALE can be used to scale one integer volume
* value into another one using only integer arithmetic. If the input
* value x is in the range 0 <= x <= xmax, then the result will be in
* the range 0 <= SCALE(xmax,ymax,x) <= ymax.
*
* This macro has for all xmax, ymax > 0 and all 0 <= x <= xmax the
* following nice properties:
*
* - SCALE(xmax,ymax,xmax) = ymax
* - SCALE(xmax,ymax,0) = 0
* - SCALE(xmax,ymax,SCALE(ymax,xmax,SCALE(xmax,ymax,x))) = SCALE(xmax,ymax,x)
*
* In addition, the rounding error is minimal and nicely distributed.
* The proofs are left as an exercise to the reader.
*/
#define SCALE(xmax,ymax,x) (((x)*(ymax)+(xmax)/2)/(xmax))
#endif /* _ACI_H_ */
此差异已折叠。
此差异已折叠。
#ifndef _AD1889_H_
#define _AD1889_H_
#define AD_DS_WSMC 0x00 /* DMA input wave/syn mixer control */
#define AD_DS_RAMC 0x02 /* DMA output resamp/ADC mixer control */
#define AD_DS_WADA 0x04 /* DMA input wave attenuation */
#define AD_DS_SYDA 0x06 /* DMA input syn attentuation */
#define AD_DS_WAS 0x08 /* wave input sample rate */
#define AD_DS_RES 0x0a /* resampler output sample rate */
#define AD_DS_CCS 0x0c /* chip control/status */
#define AD_DMA_RESBA 0x40 /* RES base addr */
#define AD_DMA_RESCA 0x44 /* RES current addr */
#define AD_DMA_RESBC 0x48 /* RES base cnt */
#define AD_DMA_RESCC 0x4c /* RES current count */
#define AD_DMA_ADCBA 0x50 /* ADC */
#define AD_DMA_ADCCA 0x54
#define AD_DMA_ADCBC 0x58
#define AD_DMA_ADCCC 0x5c
#define AD_DMA_SYNBA 0x60 /* SYN */
#define AD_DMA_SYNCA 0x64
#define AD_DMA_SYNBC 0x68
#define AD_DMA_SYNCC 0x6c
#define AD_DMA_WAVBA 0x70 /* WAV */
#define AD_DMA_WAVCA 0x74
#define AD_DMA_WAVBC 0x78
#define AD_DMA_WAVCC 0x7c
#define AD_DMA_RESICC 0x80 /* RES interrupt current count */
#define AD_DMA_RESIBC 0x84 /* RES interrupt base count */
#define AD_DMA_ADCICC 0x88 /* ADC interrupt current count */
#define AD_DMA_ADCIBC 0x8c /* ADC interrupt base count */
#define AD_DMA_SYNICC 0x90 /* SYN interrupt current count */
#define AD_DMA_SYNIBC 0x94 /* SYN interrupt base count */
#define AD_DMA_WAVICC 0x98 /* WAV interrupt current count */
#define AD_DMA_WAVIBC 0x9c /* WAV interrupt base count */
#define AD_DMA_RESCTRL 0xa0 /* RES PCI control/status */
#define AD_DMA_ADCCTRL 0xa8 /* ADC PCI control/status */
#define AD_DMA_SYNCTRL 0xb0 /* SYN PCI control/status */
#define AD_DMA_WAVCTRL 0xb8 /* WAV PCI control/status */
#define AD_DMA_DISR 0xc0 /* PCI DMA intr status */
#define AD_DMA_CHSS 0xc4 /* PCI DMA channel stop status */
#define AD_GPIO_IPC 0xc8 /* IO port ctrl */
#define AD_GPIO_OP 0xca /* IO output status */
#define AD_GPIO_IP 0xcc /* IO input status */
/* AC97 registers, 0x100 - 0x17f; see ac97.h */
#define AD_AC97_BASE 0x100 /* ac97 base register */
#define AD_AC97_ACIC 0x180 /* AC Link interface ctrl */
/* OPL3; BAR1 */
#define AD_OPL_M0AS 0x00 /* Music0 address/status */
#define AD_OPL_M0DATA 0x01 /* Music0 data */
#define AD_OPL_M1A 0x02 /* Music1 address */
#define AD_OPL_M1DATA 0x03 /* Music1 data */
/* 0x04-0x0f reserved */
/* MIDI; BAR2 */
#define AD_MIDA 0x00 /* MIDI data */
#define AD_MISC 0x01 /* MIDI status/cmd */
/* 0x02-0xff reserved */
#define AD_DS_IOMEMSIZE 512
#define AD_OPL_MEMSIZE 16
#define AD_MIDI_MEMSIZE 16
#define AD_WAV_STATE 0
#define AD_ADC_STATE 1
#define AD_MAX_STATES 2
#define DMA_SIZE (128*1024)
#define DMA_FLAG_MAPPED 1
struct ad1889_dev;
typedef struct ad1889_state {
struct ad1889_dev *card;
mode_t open_mode;
struct dmabuf {
unsigned int rate;
unsigned char fmt, enable;
/* buf management */
size_t rawbuf_size;
void *rawbuf;
dma_addr_t dma_handle; /* mapped address */
unsigned long dma_len; /* number of bytes mapped */
/* indexes into rawbuf for setting up DMA engine */
volatile unsigned long rd_ptr, wr_ptr;
wait_queue_head_t wait; /* to wait for buf servicing */
/* OSS bits */
unsigned int mapped:1;
unsigned int ready:1;
unsigned int ossfragshift;
int ossmaxfrags;
unsigned int subdivision;
} dmabuf;
struct mutex mutex;
} ad1889_state_t;
typedef struct ad1889_dev {
void __iomem *regbase;
struct pci_dev *pci;
spinlock_t lock;
int dev_audio;
/* states; one per channel; right now only WAV and ADC */
struct ad1889_state state[AD_MAX_STATES];
/* AC97 codec */
struct ac97_codec *ac97_codec;
u16 ac97_features;
/* debugging stuff */
struct stats {
unsigned int wav_intrs, adc_intrs;
unsigned int blocks, underrun, error;
} stats;
} ad1889_dev_t;
typedef struct ad1889_reg {
const char *name;
int offset;
int width;
} ad1889_reg_t;
#endif
/*
* sound/oss/adlib_card.c
*
* Detection routine for the AdLib card.
*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
* Version 2 (June 1991). See the "COPYING" file distributed with this software
* for more info.
*/
#include <linux/module.h>
#include <linux/init.h>
#include "sound_config.h"
#include "opl3.h"
static void __init attach_adlib_card(struct address_info *hw_config)
{
hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp, THIS_MODULE);
}
static int __init probe_adlib(struct address_info *hw_config)
{
return opl3_detect(hw_config->io_base, hw_config->osp);
}
static struct address_info cfg;
static int __initdata io = -1;
module_param(io, int, 0);
static int __init init_adlib(void)
{
cfg.io_base = io;
if (cfg.io_base == -1) {
printk(KERN_ERR "adlib: must specify I/O address.\n");
return -EINVAL;
}
if (probe_adlib(&cfg) == 0)
return -ENODEV;
attach_adlib_card(&cfg);
return 0;
}
static void __exit cleanup_adlib(void)
{
sound_unload_synthdev(cfg.slots[0]);
}
module_init(init_adlib);
module_exit(cleanup_adlib);
#ifndef MODULE
static int __init setup_adlib(char *str)
{
/* io */
int ints[2];
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
return 1;
}
__setup("adlib=", setup_adlib);
#endif
MODULE_LICENSE("GPL");
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# Makefile for Creative Labs EMU10K1
#
# 12 Apr 2000 Rui Sousa
obj-$(CONFIG_SOUND_EMU10K1) += emu10k1.o
emu10k1-objs := audio.o cardmi.o cardmo.o cardwi.o cardwo.o ecard.o \
efxmgr.o emuadxmg.o hwaccess.o irqmgr.o main.o midi.o \
mixer.o passthrough.o recmgr.o timer.o voicemgr.o
ifdef DEBUG
EXTRA_CFLAGS += -DEMU10K1_DEBUG
endif
ifdef CONFIG_MIDI_EMU10K1
EXTRA_CFLAGS += -DEMU10K1_SEQUENCER
endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
int opl3_detect (int ioaddr, int *osp);
int opl3_init(int ioaddr, int *osp, struct module *owner);
void enable_opl3_mode(int left, int right, int both);
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册