Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

369
sound/oss/CHANGELOG Normal file
View File

@@ -0,0 +1,369 @@
Note these changes relate to Hannu's code and don't include the changes
made outside of this for modularising the sound
Changelog for version 3.8o
--------------------------
Since 3.8h
- Included support for OPL3-SA1 and SoftOSS
Since 3.8
- Fixed SNDCTL_DSP_GETOSPACE
- Compatibility fixes for Linux 2.1.47
Since 3.8-beta21
- Fixed all known bugs (I think).
Since 3.8-beta8
- Lot of fixes to audio playback code in dmabuf.c
Since 3.8-beta6
- Fixed the famous Quake delay bug.
Since 3.8-beta5
- Fixed many bugs in audio playback.
Since 3.8-beta4
- Just minor changes.
Since 3.8-beta1
- Major rewrite of audio playback handling.
- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
Since 3.7-beta#
- Passing of ioctl() parameters between soundcard.c and other modules has been
changed so that arg always points to kernel space.
- Some bugfixes.
Since 3.7-beta5
- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
stream of received 0x00 bytes when the MIDI receiver is enabled.
Since 3.5
- Changes almost everywhere.
- Support for OPTi 82C924-based sound cards.
Since 3.5.4-beta8
- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
with GUS.
- Limited minimum fragment size with some audio devices (GUS=512 and
SB=32). These devices require more time to "recover" from processing
of each fragment.
Since 3.5.4-beta6/7
- There seems to be problems in the OPTi 82C930 so cards based on this
chip don't necessarily work yet. There are problems in detecting the
MIDI interface. Also mixer volumes may be seriously wrong on some systems.
You can safely use this driver version with C930 if it looks to work.
However please don't complain if you have problems with it. C930 support
should be fixed in future releases.
- Got initialization of GUS PnP to work. With this version GUS PnP should
work in GUS compatible mode after initialization using isapnptools.
- Fixed a bug in handling of full duplex cards in write only mode. This has
been causing "audio device opening" errors with RealAudio player.
Since 3.5.4.beta5
- Changes to OPTi 82C930 driver.
- Major changes to the Soundscape driver. The driver requires now just one
DMA channel. The extra audio/dsp device (the "Not functional" one) used
for code download in the earlier versions has been eliminated. There is now
just one /dev/dsp# device which is used both for code download and audio.
Since 3.5.4.beta4
- Minor changes.
Since 3.5.4-beta2
- Fixed silent playback with ESS 688/1688.
- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
is required for 8 and 16 bit modes).
- Added the "lowlevel" subdirectory for additional low level drivers that
are not part of USS core. See lowlevel/README for more info.
- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
miroPCM sound cards. See lowlevel/aci.readme for more info.
- Support for Aztech Washington chipset (AZT2316 ASIC).
Since 3.5.4-beta1
- Reduced clicking with AD1848.
- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
is sometimes just white noise (occurs randomly).
Since 3.5.2
- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
The most noticeable new feature is support for multiple SB cards at the same
time.
- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
other MPU401 UART compatible cards than SB16/ESS/Jazz.
- Some changes which reduce clicking in audio playback.
- Copying policy is now GPL.
Since 3.5.1
- TB Maui initialization support
Since 3.5
- Improved handling of playback underrun situations.
Since 3.5-beta10
- Bug fixing
Since 3.5-beta9
- Fixed for compatibility with Linux 1.3.70 and later.
- Changed boot time passing of 16 bit DMA channel number to SB driver.
Since 3.5-beta8
- Minor changes
Since 3.5-beta7
- enhancements to configure program (by Jeff Tranter):
- prompts are in same format as 1.3.x Linux kernel config program
- on-line help for each question
- fixed some compile warnings detected by gcc/g++ -Wall
- minor grammatical changes to prompts
Since 3.5-beta6
- Fixed bugs in mmap() support.
- Minor changes to Maui driver.
Since 3.5-beta5
- Fixed crash after recording with ESS688. It's generally a good
idea to stop inbound DMA transfers before freeing the memory
buffer.
- Fixed handling of AD1845 codec (for example Shuttle Sound System).
- Few other fixes.
Since 3.5-beta4
- Fixed bug in handling of uninitialized instruments with GUS.
Since 3.5-beta3
- Few changes which decrease popping at end/beginning of audio playback.
Since 3.5-beta2
- Removed MAD16+CS4231 hack made in previous version since it didn't
help.
- Fixed the above bug in proper way and in proper place. Many thanks
to James Hightower.
Since 3.5-beta1
- Bug fixes.
- Full duplex audio with MAD16+CS4231 may work now. The driver configures
SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
The side effect is that all 8 bit DMA channels (0,1,3) are populated in
duplex mode.
Since 3.5-alpha9
- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
- Temporarily disabled recording with ESS1688/688 since it causes crash.
- Changed audio buffer partitioning algorithm so that it selects
smaller fragment size than earlier. This improves real time capabilities
of the driver and makes recording to disk to work better. Unfortunately
this change breaks some programs which assume that fragments cannot be
shorter than 4096 bytes.
Since 3.5-alpha8
- Bug fixes
Since 3.5-alpha7
- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
using command "cd /linux/drivers/sound;make script" and then
just run kernel's make config normally.
- Minor fixes to the SB support. Hopefully the driver works with
all SB models now.
- Added support for ESS ES1688 "AudioDrive" based cards.
Since 3.5-alpha6
- SB Pro and SB16 supports are no longer separately selectable options.
Enabling SB enables them too.
- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
configure to handle this.
- Removed initialization messages from the
modularized version. They can be enabled by using init_trace=1 in
the insmod command line (insmod sound init_trace=1).
- More AIX stuff.
- Added support for synchronizing dsp/audio devices with /dev/sequencer.
- mmap() support for dsp/audio devices.
Since 3.5-alpha5
- AIX port.
- Changed some xxx_PATCH macros in soundcard.h to work with
big endian machines.
Since 3.5-alpha4
- Removed the 'setfx' stuff from the version distributed with kernel
sources. Running 'setfx' is required again.
Since 3.5-alpha3
- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
Since 3.5-alpha2
- Modifications to makefile and configure.c. Unnecessary sources
are no longer compiled. Newly created local.h is also copied to
/etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
new local.h which is compatible with current version of the driver.
- Some fixes to the SB16 support.
- Fixed random protection fault in gus_wave.c
Since 3.5-alpha1
- Modified to work with Linux-1.3.33 and later
- Some minor changes
Since 3.0.2
- Support for CS4232 based PnP cards (AcerMagic S23 etc).
- Full duplex support for some CS4231, CS4232 and AD1845 based cards
(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
having a codec mentioned above).
- Almost fully rewritten loadable modules support.
- Fixed some bugs.
- Huge amount of testing (more testing is still required).
- mmap() support (works with some cards). Requires much more testing.
- Sample/patch/program loading for TB Maui/Tropez. No initialization
since TB doesn't allow me to release that code.
- Using CS4231 compatible codecs as timer for /dev/music.
Since 3.0.1
- Added allocation of I/O ports, DMA channels and interrupts
to the initialization code. This may break modules support since
the driver may not free some resources on unload. Should be fixed soon.
Since 3.0
- Some important bug fixes.
- select() for /dev/dsp and /dev/audio (Linux only).
(To use select() with read, you have to call read() to start
the recording. Calling write() kills recording immediately so
use select() carefully when you are writing a half duplex app.
Full duplex mode is not implemented yet.) Select works also with
/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
Since 3.0-beta2
- Minor fixes.
- Added Readme.cards
Since 3.0-beta1
- Minor fixes to the modules support.
- Eliminated call to sb_free_irq() in ad1848.c
- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
- Fix to DMA initialization of PSS cards.
- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
- Fixed some bugs in the PSS driver which caused I/O errors with
the MSS mode (/dev/dsp).
Since 3.0-950506
- Recording with GUS MAX fixed. It works when the driver is configured
to use two DMA channels with GUS MAX (16 bit ones recommended).
Since 3.0-94xxxx
- Too many changes
Since 3.0-940818
- Fixes for Linux 1.1.4x.
- Disables Disney Sound System with SG NX Pro 16 (less noise).
Since 2.90-2
- Fixes to soundcard.h
- Non blocking mode to /dev/sequencer
- Experimental detection code for Ensoniq Soundscape.
Since 2.90
- Minor and major bug fixes
Since pre-3.0-940712
- GUS MAX support
- Partially working MSS/WSS support (could work with some cards).
- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
(GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
GUS MAX, but it doesn't work yet.
Since pre-3.0-940426
- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
This codec chip is used in various sound cards. This version is developed
for the 16 bit daughtercard of GUS. It should work with other cards also
if the following requirements are met:
- The I/O, IRQ and DMA settings are jumper selectable or
the card is initialized by booting DOS before booting Linux (etc.).
- You add the IO, IRQ and DMA settings manually to the local.h.
(Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
the base address bust be the base address of the codec chip not the
card itself. For the GUS16 these are the same but most MSS compatible
cards have the codec located at card_base+4.
- Some minor changes
Since 2.5 (******* MAJOR REWRITE ***********)
This version is based on v2.3. I have tried to maintain two versions
together so that this one should have the same features than v2.5.
Something may still be missing. If you notice such things, please let me
know.
The Readme.v30 contains more details.
- /dev/midi## devices.
- /dev/sequencer2
Since 2.5-beta2
- Some fine tuning to the GUS v3.7 mixer code.
- Fixed speed limits for the plain SB (1.0 to 2.0).
Since 2.5-beta
- Fixed OPL-3 detection with SB. Caused problems with PAS16.
- GUS v3.7 mixer support.
Since 2.4
- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
- Fixed truncated sound on /dev/dsp when the device is closed.
- Linear volume mode for GUS
- Pitch bends larger than +/- 2 octaves.
- MIDI recording for SB and SB Pro. (Untested).
- Some other fixes.
- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
- Implemented better detection for OPL-3. This should be useful if you
have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
Since 2.3b
- Fixed bug which made it impossible to make long recordings to disk.
Recording was not restarted after a buffer overflow situation.
- Limited mixer support for GUS.
- Numerous improvements to the GUS driver by Andrew Robinson. Including
some click removal etc.
Since 2.3
- Fixed some minor bugs in the SB16 driver.
Since 2.2b
- Full SB16 DSP support. 8/16 bit, mono/stereo
- The SCO and FreeBSD versions should be in sync now. There are some
problems with SB16 and GUS in the FreeBSD versions.
The DMA buffer allocation of the SCO version has been polished but
there could still be some problems. At least it hogs memory.
The DMA channel
configuration method used in the SCO/System is a hack.
- Support for the MPU emulation of the SB16.
- Some big arrays are now allocated boot time. This makes the BSS segment
smaller which makes it possible to use the full driver with
NetBSD. These arrays are not allocated if no suitable sound card is available.
- Fixed a bug in the compute_and_set_volume in gus_wave.c
- Fixed the too fast mono playback problem of SB Pro and PAS16.
Since 2.2
- Stereo recording for SB Pro. Somehow it was missing and nobody
had noticed it earlier.
- Minor polishing.
- Interpreting of boot time arguments (sound=) for Linux.
- Breakup of sb_dsp.c. Parts of the code has been moved to
sb_mixer.c and sb_midi.c
Since 2.1
- Preliminary support for SB16.
- The SB16 mixer is supported in its native mode.
- Digitized voice capability up to 44.1 kHz/8 bit/mono
(16 bit and stereo support coming in the next release).
- Fixed some bugs in the digitized voice driver for PAS16.
- Proper initialization of the SB emulation of latest PAS16 models.
- Significantly improved /dev/dsp and /dev/audio support.
- Now supports half duplex mode. It's now possible to record and
playback without closing and reopening the device.
- It's possible to use smaller buffers than earlier. There is a new
ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
This call instructs the driver to use smaller buffers. The default
buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
immediately after opening the device.
Since 2.0
Just cosmetic changes.

339
sound/oss/COPYING Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1120
sound/oss/Kconfig Normal file

File diff suppressed because it is too large Load Diff

187
sound/oss/Makefile Normal file
View File

@@ -0,0 +1,187 @@
# Makefile for the Linux sound card driver
#
# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
# Rewritten to use lists instead of if-statements.
# Each configuration option enables a list of files.
obj-$(CONFIG_SOUND_OSS) += sound.o
obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o
# Please leave it as is, cause the link order is significant !
obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
obj-$(CONFIG_SOUND_HAL2) += hal2.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
obj-$(CONFIG_SOUND_OPL3SA1) += opl3sa.o ad1848.o uart401.o
obj-$(CONFIG_SOUND_SSCAPE) += sscape.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_MAD16) += mad16.o ad1848.o sb_lib.o uart401.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_WAVEFRONT) += wavefront.o
obj-$(CONFIG_SOUND_MAUI) += maui.o mpu401.o
obj-$(CONFIG_SOUND_MPU401) += mpu401.o
obj-$(CONFIG_SOUND_UART6850) += uart6850.o
obj-$(CONFIG_SOUND_GUS) += gus.o ad1848.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_SGALAXY) += sgalaxy.o ad1848.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_AWE32_SYNTH) += awe_wave.o
obj-$(CONFIG_SOUND_VIA82CXXX) += via82cxxx_audio.o ac97_codec.o
ifeq ($(CONFIG_MIDI_VIA82CXXX),y)
obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o
endif
obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o
ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y)
obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o
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_SONICVIBES) += sonicvibes.o
obj-$(CONFIG_SOUND_CMPCI) += cmpci.o
ifeq ($(CONFIG_SOUND_CMPCI_FM),y)
obj-$(CONFIG_SOUND_CMPCI) += sound.o opl3.o
endif
ifeq ($(CONFIG_SOUND_CMPCI_MIDI),y)
obj-$(CONFIG_SOUND_CMPCI) += sound.o mpu401.o
endif
obj-$(CONFIG_SOUND_ES1370) += es1370.o
obj-$(CONFIG_SOUND_ES1371) += es1371.o ac97_codec.o
obj-$(CONFIG_SOUND_VRC5477) += nec_vrc5477.o ac97_codec.o
obj-$(CONFIG_SOUND_AU1000) += au1000.o ac97_codec.o
obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o
obj-$(CONFIG_SOUND_FUSION) += cs46xx.o ac97_codec.o
obj-$(CONFIG_SOUND_MAESTRO) += maestro.o
obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o
obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o
obj-$(CONFIG_SOUND_HARMONY) += harmony.o
obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o
obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o
obj-$(CONFIG_SOUND_BT878) += btaudio.o
obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o
obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o
obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o
obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.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_SOUND_CS4281) += cs4281/
obj-$(CONFIG_DMASOUND) += dmasound/
# Declare multi-part drivers.
sound-objs := \
dev_table.o soundcard.o sound_syms.o \
audio.o audio_syms.o dmabuf.o \
midi_syms.o midi_synth.o midibuf.o \
sequencer.o sequencer_syms.o sound_timer.o sys_timer.o
gus-objs := gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
sb-objs := sb_card.o
sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
vidc_mod-objs := vidc.o vidc_fill.o
wavefront-objs := wavfront.o wf_midi.o yss225.o
hostprogs-y := bin2hex hex2hex
# Files generated that shall be removed upon make clean
clean-files := maui_boot.h msndperm.c msndinit.c pndsperm.c pndspini.c \
pss_boot.h trix_boot.h
# Firmware files that need translation
#
# The translated files are protected by a file that keeps track
# of what name was used to build them. If the name changes, they
# will be forced to be remade.
#
# Turtle Beach Maui / Tropez
$(obj)/maui.o: $(obj)/maui_boot.h
ifeq ($(CONFIG_MAUI_HAVE_BOOT),y)
$(obj)/maui_boot.h: $(patsubst "%", %, $(CONFIG_MAUI_BOOT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex -i maui_os < $< > $@
else
$(obj)/maui_boot.h:
( \
echo 'static unsigned char * maui_os = NULL;'; \
echo 'static int maui_osLen = 0;'; \
) > $@
endif
# Turtle Beach MultiSound
ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
$(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c
$(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex
$(obj)/bin2hex msndperm < $< > $@
$(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex msndinit < $< > $@
endif
ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
$(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c
$(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pndsperm < $< > $@
$(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pndspini < $< > $@
endif
# PSS (ECHO-ADI2111)
$(obj)/pss.o: $(obj)/pss_boot.h
ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
$(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex
$(obj)/bin2hex pss_synth < $< > $@
else
$(obj)/pss_boot.h:
( \
echo 'static unsigned char * pss_synth = NULL;'; \
echo 'static int pss_synthLen = 0;'; \
) > $@
endif
# MediaTrix AudioTrix Pro
$(obj)/trix.o: $(obj)/trix_boot.h
ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
$(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex
$(obj)/hex2hex -i trix_boot < $< > $@
else
$(obj)/trix_boot.h:
( \
echo 'static unsigned char * trix_boot = NULL;'; \
echo 'static int trix_boot_len = 0;'; \
) > $@
endif

6
sound/oss/README.FIRST Normal file
View File

@@ -0,0 +1,6 @@
The modular sound driver patches were funded by Red Hat Software
(www.redhat.com). The sound driver here is thus a modified version of
Hannu's code. Please bear that in mind when considering the appropriate
forums for bug reporting.
Alan Cox

452
sound/oss/ac97.c Normal file
View File

@@ -0,0 +1,452 @@
#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;
}
/* Reset the mixer to the currently saved settings. */
int
ac97_reset (struct ac97_hwint *dev)
{
int x;
if (dev->reset_device (dev))
return -1;
/* Now set the registers back to their last-written values. */
for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
int regnum = mixerRegs[x].ac97_regnum;
int value = dev->last_written_mixer_values [regnum / 2];
if (value >= 0)
ac97_put_register (dev, regnum, value);
}
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);
EXPORT_SYMBOL(ac97_reset);
MODULE_LICENSE("GPL");
/*
* Local variables:
* c-basic-offset: 4
* End:
*/

204
sound/oss/ac97.h Normal file
View File

@@ -0,0 +1,204 @@
/*
* 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);
/* Do a complete reset on the AC97 mixer, restoring all mixer registers to
the current values. Normally used after an APM resume event. */
extern int ac97_reset (struct ac97_hwint *dev);
#endif
/*
* Local variables:
* c-basic-offset: 4
* End:
*/

1576
sound/oss/ac97_codec.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
/*
ac97_plugin_ad1980.c Copyright (C) 2003 Red Hat, Inc. All rights reserved.
The contents of this file are subject to the Open Software License version 1.1
that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is
included herein by reference.
Alternatively, the contents of this file may be used under the
terms of the GNU General Public License version 2 (the "GPL") as
distributed in the kernel source COPYING file, in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the OSL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the OSL or the GPL.
Authors: Alan Cox <alan@redhat.com>
This is an example codec plugin. This one switches the connections
around to match the setups some vendors use with audio switched to
non standard front connectors not the normal rear ones
This code primarily exists to demonstrate how to use the codec
interface
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ac97_codec.h>
/**
* ad1980_remove - codec remove callback
* @codec: The codec that is being removed
*
* This callback occurs when an AC97 codec is being removed. A
* codec remove call will not occur for a codec during that codec
* probe callback.
*
* Most drivers will need to lock their remove versus their
* use of the codec after the probe function.
*/
static void __devexit ad1980_remove(struct ac97_codec *codec, struct ac97_driver *driver)
{
/* Nothing to do in the simple example */
}
/**
* ad1980_probe - codec found callback
* @codec: ac97 codec matching the idents
* @driver: ac97_driver it matched
*
* This entry point is called when a codec is found which matches
* the driver. At the point it is called the codec is basically
* operational, mixer operations have been initialised and can
* be overriden. Called in process context. The field driver_private
* is available for the driver to use to store stuff.
*
* The caller can claim the device by returning zero, or return
* a negative error code.
*/
static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver)
{
u16 control;
#define AC97_AD_MISC 0x76
/* Switch the inputs/outputs over (from Dell code) */
control = codec->codec_read(codec, AC97_AD_MISC);
codec->codec_write(codec, AC97_AD_MISC, control | 0x4420);
/* We could refuse the device since we dont need to hang around,
but we will claim it */
return 0;
}
static struct ac97_driver ad1980_driver = {
.codec_id = 0x41445370,
.codec_mask = 0xFFFFFFFF,
.name = "AD1980 example",
.probe = ad1980_probe,
.remove = __devexit_p(ad1980_remove),
};
/**
* ad1980_exit - module exit path
*
* Our module is being unloaded. At this point unregister_driver
* will call back our remove handler for any existing codecs. You
* may not unregister_driver from interrupt context or from a
* probe/remove callback.
*/
static void ad1980_exit(void)
{
ac97_unregister_driver(&ad1980_driver);
}
/**
* ad1980_init - set up ad1980 handlers
*
* After we call the register function it will call our probe
* function for each existing matching device before returning to us.
* Any devices appearing afterwards whose id's match the codec_id
* will also cause the probe function to be called.
* You may not register_driver from interrupt context or from a
* probe/remove callback.
*/
static int ad1980_init(void)
{
return ac97_register_driver(&ad1980_driver);
}
module_init(ad1980_init);
module_exit(ad1980_exit);
MODULE_LICENSE("GPL");

711
sound/oss/aci.c Normal file
View File

@@ -0,0 +1,711 @@
/*
* 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 <asm/semaphore.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 semaphore aci_sem;
#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 (down_interruptible(&aci_sem))
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: up(&aci_sem);
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;
init_MUTEX(&aci_sem);
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");

57
sound/oss/aci.h Normal file
View File

@@ -0,0 +1,57 @@
#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_ */

1369
sound/oss/ad1816.c Normal file

File diff suppressed because it is too large Load Diff

3159
sound/oss/ad1848.c Normal file

File diff suppressed because it is too large Load Diff

25
sound/oss/ad1848.h Normal file
View File

@@ -0,0 +1,25 @@
#include <linux/interrupt.h>
#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */
#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */
#define AD1848_SET_XTAL 1
#define AD1848_MIXER_REROUTE 2
#define AD1848_REROUTE(oldctl, newctl) \
ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback,
int dma_capture, int share_dma, int *osp, struct module *owner);
void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
int ad1848_detect (struct resource *ports, int *flags, int *osp);
int ad1848_control(int cmd, int arg);
irqreturn_t adintr(int irq, void *dev_id, struct pt_regs * dummy);
void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner);
int probe_ms_sound(struct address_info *hw_config, struct resource *ports);
void unload_ms_sound(struct address_info *hw_info);

253
sound/oss/ad1848_mixer.h Normal file
View File

@@ -0,0 +1,253 @@
/*
* sound/ad1848_mixer.h
*
* Definitions for the mixer of AD1848 and compatible codecs.
*/
/*
* 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.
*/
/*
* The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
* Sound card manufacturers have connected actual inputs (CD, synth, line,
* etc) to these inputs in different order. Therefore it's difficult
* to assign mixer channels to these inputs correctly. The following
* contains two alternative mappings. The first one is for GUS MAX and
* the second is just a generic one (line1, line2 and line3).
* (Actually this is not a mapping but rather some kind of interleaving
* solution).
*/
#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_LINE1)
#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
SOUND_MASK_LINE2 | \
SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
SOUND_MASK_MIC | \
SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
SOUND_MASK_IGAIN | \
SOUND_MASK_PCM | SOUND_MASK_IMIX)
#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
* input
*/
#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
SOUND_MASK_LINE3 | \
SOUND_MASK_IGAIN | SOUND_MASK_PCM)
#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
SOUND_MASK_CD | SOUND_MASK_MIC | \
SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
SOUND_MASK_OGAIN)
struct mixer_def {
unsigned int regno:6; /* register number for volume */
unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */
unsigned int bitpos:3; /* position of bits in register for volume */
unsigned int nbits:3; /* number of bits in register for volume */
unsigned int mutereg:6; /* register number for mute bit */
unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */
unsigned int mutepos:4; /* position of mute bit in register */
unsigned int recreg:6; /* register number for recording bit */
unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */
unsigned int recpos:4; /* position of recording bit in register */
};
static char mix_cvt[101] = {
0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
100
};
typedef struct mixer_def mixer_ent;
typedef mixer_ent mixer_ents[2];
/*
* Most of the mixer entries work in backwards. Setting the polarity field
* makes them to work correctly.
*
* The channel numbering used by individual sound cards is not fixed. Some
* cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
* The current version doesn't try to compensate this.
*/
#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \
[name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \
{reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
rec_reg_l, rec_pola_l, rec_pos_l, \
reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
rec_reg_r, rec_pola_r, rec_pos_r) \
[name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
rec_reg_l, rec_pola_l, rec_pos_l}, \
{reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
rec_reg_r, rec_pola_r, rec_pos_r}}
static mixer_ents ad1848_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
};
static mixer_ents iwave_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
};
static mixer_ents cs42xb_mix_devices[32] = {
/* Digital master volume actually has seven bits, but we only use
six to avoid the discontinuity when the analog gain kicks in. */
MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
/* For the IMIX entry, it was not possible to use the MIX_ENT macro
because the mute bit is in different positions for the two
channels and requires reverse polarity. */
[SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
{42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7)
};
/* OPTi 82C930 has somewhat different port addresses.
* Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
* VOLUME, SYNTH, LINE, CD are not enabled above.
* MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
*/
static mixer_ents c930_mix_devices[32] = {
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7),
MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7),
MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7),
MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7)
};
static mixer_ents spro_mix_devices[32] = {
MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8),
MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8,
5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8),
MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8),
MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8),
/* This is external wavetable */
MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
};
static int default_mixer_levels[32] =
{
0x3232, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x3232, /* PCM */
0x1515, /* PC Speaker */
0x2020, /* Ext Line */
0x1010, /* Mic */
0x4b4b, /* CD */
0x0000, /* Recording monitor */
0x4b4b, /* Second PCM */
0x4b4b, /* Recording level */
0x4b4b, /* Input gain */
0x4b4b, /* Output gain */
0x2020, /* Line1 */
0x2020, /* Line2 */
0x1515 /* Line3 (usually line in)*/
};
#define LEFT_CHN 0
#define RIGHT_CHN 1
/*
* Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
*/
#ifndef AUDIO_SPEAKER
#define AUDIO_SPEAKER 0x01 /* Enable mono output */
#define AUDIO_HEADPHONE 0x02 /* Sparc only */
#define AUDIO_LINE_OUT 0x04 /* Sparc only */
#endif

1103
sound/oss/ad1889.c Normal file

File diff suppressed because it is too large Load Diff

134
sound/oss/ad1889.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef _AD1889_H_
#define _AD1889_H_
#define AD_DSWSMC 0x00 /* DMA input wave/syn mixer control */
#define AD_DSRAMC 0x02 /* DMA output resamp/ADC mixer control */
#define AD_DSWADA 0x04 /* DMA input wave attenuation */
#define AD_DSSYDA 0x06 /* DMA input syn attentuation */
#define AD_DSWAS 0x08 /* wave input sample rate */
#define AD_DSRES 0x0a /* resampler output sample rate */
#define AD_DSCCS 0x0c /* chip control/status */
#define AD_DMARESBA 0x40 /* RES base addr */
#define AD_DMARESCA 0x44 /* RES current addr */
#define AD_DMARESBC 0x48 /* RES base cnt */
#define AD_DMARESCC 0x4c /* RES current count */
#define AD_DMAADCBA 0x50 /* ADC */
#define AD_DMAADCCA 0x54
#define AD_DMAADCBC 0x58
#define AD_DMAADCCC 0x5c
#define AD_DMASYNBA 0x60 /* SYN */
#define AD_DMASYNCA 0x64
#define AD_DMASYNBC 0x68
#define AD_DMASYNCC 0x6c
#define AD_DMAWAVBA 0x70 /* WAV */
#define AD_DMAWAVCA 0x74
#define AD_DMAWAVBC 0x78
#define AD_DMAWAVCC 0x7c
#define AD_DMARESICC 0x80 /* RES interrupt current count */
#define AD_DMARESIBC 0x84 /* RES interrupt base count */
#define AD_DMAADCICC 0x88 /* ADC interrupt current count */
#define AD_DMAADCIBC 0x8c /* ADC interrupt base count */
#define AD_DMASYNICC 0x90 /* SYN interrupt current count */
#define AD_DMASYNIBC 0x94 /* SYN interrupt base count */
#define AD_DMAWAVICC 0x98 /* WAV interrupt current count */
#define AD_DMAWAVIBC 0x9c /* WAV interrupt base count */
#define AD_DMARESCTRL 0xa0 /* RES PCI control/status */
#define AD_DMAADCCTRL 0xa8 /* ADC PCI control/status */
#define AD_DMASYNCTRL 0xb0 /* SYN PCI control/status */
#define AD_DMAWAVCTRL 0xb8 /* WAV PCI control/status */
#define AD_DMADISR 0xc0 /* PCI DMA intr status */
#define AD_DMACHSS 0xc4 /* PCI DMA channel stop status */
#define AD_GPIOIPC 0xc8 /* IO port ctrl */
#define AD_GPIOOP 0xca /* IO output status */
#define AD_GPIOIP 0xcc /* IO input status */
/* AC97 registers, 0x100 - 0x17f; see ac97.h */
#define AD_ACIC 0x180 /* AC Link interface ctrl */
/* OPL3; BAR1 */
#define AD_OPLM0AS 0x00 /* Music0 address/status */
#define AD_OPLM0DATA 0x01 /* Music0 data */
#define AD_OPLM1A 0x02 /* Music1 address */
#define AD_OPLM1DATA 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_DSIOMEMSIZE 512
#define AD_OPLMEMSIZE 16
#define AD_MIDIMEMSIZE 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 semaphore sem;
} 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

73
sound/oss/adlib_card.c Normal file
View File

@@ -0,0 +1,73 @@
/*
* sound/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");

1381
sound/oss/aedsp16.c Normal file

File diff suppressed because it is too large Load Diff

3733
sound/oss/ali5455.c Normal file

File diff suppressed because it is too large Load Diff

2214
sound/oss/au1000.c Normal file

File diff suppressed because it is too large Load Diff

2119
sound/oss/au1550_ac97.c Normal file

File diff suppressed because it is too large Load Diff

983
sound/oss/audio.c Normal file
View File

@@ -0,0 +1,983 @@
/*
* sound/audio.c
*
* Device file manager for /dev/audio
*/
/*
* 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.
*/
/*
* Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
* Thomas Sailer : moved several static variables into struct audio_operations
* (which is grossly misnamed btw.) because they have the same
* lifetime as the rest in there and dynamic allocation saves
* 12k or so
* Thomas Sailer : use more logical O_NONBLOCK semantics
* Daniel Rodriksson: reworked the use of the device specific copy_user
* still generic
* Horst von Brand: Add missing #include <linux/string.h>
* Chris Rankin : Update the module-usage counter for the coprocessor,
* and decrement the counters again if we cannot open
* the audio device.
*/
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/kmod.h>
#include "sound_config.h"
#include "ulaw.h"
#include "coproc.h"
#define NEUTRAL8 0x80
#define NEUTRAL16 0x00
static int dma_ioctl(int dev, unsigned int cmd, void __user *arg);
static int set_format(int dev, int fmt)
{
if (fmt != AFMT_QUERY)
{
audio_devs[dev]->local_conversion = 0;
if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
{
if (fmt == AFMT_MU_LAW)
{
fmt = AFMT_U8;
audio_devs[dev]->local_conversion = CNV_MU_LAW;
}
else
fmt = AFMT_U8; /* This is always supported */
}
audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
audio_devs[dev]->local_format = fmt;
}
else
return audio_devs[dev]->local_format;
if (audio_devs[dev]->local_conversion)
return audio_devs[dev]->local_conversion;
else
return audio_devs[dev]->local_format;
}
int audio_open(int dev, struct file *file)
{
int ret;
int bits;
int dev_type = dev & 0x0f;
int mode = translate_mode(file);
const struct audio_driver *driver;
const struct coproc_operations *coprocessor;
dev = dev >> 4;
if (dev_type == SND_DEV_DSP16)
bits = 16;
else
bits = 8;
if (dev < 0 || dev >= num_audiodevs)
return -ENXIO;
driver = audio_devs[dev]->d;
if (!try_module_get(driver->owner))
return -ENODEV;
if ((ret = DMAbuf_open(dev, mode)) < 0)
goto error_1;
if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
if (!try_module_get(coprocessor->owner))
goto error_2;
if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
goto error_3;
}
}
audio_devs[dev]->local_conversion = 0;
if (dev_type == SND_DEV_AUDIO)
set_format(dev, AFMT_MU_LAW);
else
set_format(dev, bits);
audio_devs[dev]->audio_mode = AM_NONE;
return 0;
/*
* Clean-up stack: this is what needs (un)doing if
* we can't open the audio device ...
*/
error_3:
module_put(coprocessor->owner);
error_2:
DMAbuf_release(dev, mode);
error_1:
module_put(driver->owner);
return ret;
}
static void sync_output(int dev)
{
int p, i;
int l;
struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
if (dmap->fragment_size <= 0)
return;
dmap->flags |= DMA_POST;
/* Align the write pointer with fragment boundaries */
if ((l = dmap->user_counter % dmap->fragment_size) > 0)
{
int len;
unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
len = dmap->fragment_size - l;
memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
DMAbuf_move_wrpointer(dev, len);
}
/*
* Clean all unused buffer fragments.
*/
p = dmap->qtail;
dmap->flags |= DMA_POST;
for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
{
p = (p + 1) % dmap->nbufs;
if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
(dmap->raw_buf + dmap->buffsize))
printk(KERN_ERR "audio: Buffer error 2\n");
memset(dmap->raw_buf + p * dmap->fragment_size,
dmap->neutral_byte,
dmap->fragment_size);
}
dmap->flags |= DMA_DIRTY;
}
void audio_release(int dev, struct file *file)
{
const struct coproc_operations *coprocessor;
int mode = translate_mode(file);
dev = dev >> 4;
/*
* We do this in DMAbuf_release(). Why are we doing it
* here? Why don't we test the file mode before setting
* both flags? DMAbuf_release() does.
* ...pester...pester...pester...
*/
audio_devs[dev]->dmap_out->closing = 1;
audio_devs[dev]->dmap_in->closing = 1;
/*
* We need to make sure we allocated the dmap_out buffer
* before we go mucking around with it in sync_output().
*/
if (mode & OPEN_WRITE)
sync_output(dev);
if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
coprocessor->close(coprocessor->devc, COPR_PCM);
module_put(coprocessor->owner);
}
DMAbuf_release(dev, mode);
module_put(audio_devs[dev]->d->owner);
}
static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
{
unsigned long i;
if (n <= 0)
return;
for (i = 0; i < n; ++i)
buff[i] = table[buff[i]];
}
int audio_write(int dev, struct file *file, const char __user *buf, int count)
{
int c, p, l, buf_size, used, returned;
int err;
char *dma_buf;
dev = dev >> 4;
p = 0;
c = count;
if(count < 0)
return -EINVAL;
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EPERM;
if (audio_devs[dev]->flags & DMA_DUPLEX)
audio_devs[dev]->audio_mode |= AM_WRITE;
else
audio_devs[dev]->audio_mode = AM_WRITE;
if (!count) /* Flush output */
{
sync_output(dev);
return 0;
}
while (c)
{
if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
{
/* Handle nonblocking mode */
if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */
return err;
}
l = c;
if (l > buf_size)
l = buf_size;
returned = l;
used = l;
if (!audio_devs[dev]->d->copy_user)
{
if ((dma_buf + l) >
(audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
{
printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
return -EDOM;
}
if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
{
printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
return -EDOM;
}
if(copy_from_user(dma_buf, &(buf)[p], l))
return -EFAULT;
}
else audio_devs[dev]->d->copy_user (dev,
dma_buf, 0,
buf, p,
c, buf_size,
&used, &returned,
l);
l = returned;
if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
{
translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
}
c -= used;
p += used;
DMAbuf_move_wrpointer(dev, l);
}
return count;
}
int audio_read(int dev, struct file *file, char __user *buf, int count)
{
int c, p, l;
char *dmabuf;
int buf_no;
dev = dev >> 4;
p = 0;
c = count;
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return -EPERM;
if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
sync_output(dev);
if (audio_devs[dev]->flags & DMA_DUPLEX)
audio_devs[dev]->audio_mode |= AM_READ;
else
audio_devs[dev]->audio_mode = AM_READ;
while(c)
{
if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
{
/*
* Nonblocking mode handling. Return current # of bytes
*/
if (p > 0) /* Avoid throwing away data */
return p; /* Return it instead */
if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
return -EAGAIN;
return buf_no;
}
if (l > c)
l = c;
/*
* Insert any local processing here.
*/
if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
{
translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
}
{
char *fixit = dmabuf;
if(copy_to_user(&(buf)[p], fixit, l))
return -EFAULT;
};
DMAbuf_rmchars(dev, buf_no, l);
p += l;
c -= l;
}
return count - c;
}
int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
{
int val, count;
unsigned long flags;
struct dma_buffparms *dmap;
int __user *p = arg;
dev = dev >> 4;
if (_IOC_TYPE(cmd) == 'C') {
if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
/* else
printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
return -ENXIO;
}
else switch (cmd)
{
case SNDCTL_DSP_SYNC:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return 0;
if (audio_devs[dev]->dmap_out->fragment_size == 0)
return 0;
sync_output(dev);
DMAbuf_sync(dev);
DMAbuf_reset(dev);
return 0;
case SNDCTL_DSP_POST:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return 0;
if (audio_devs[dev]->dmap_out->fragment_size == 0)
return 0;
audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
sync_output(dev);
dma_ioctl(dev, SNDCTL_DSP_POST, NULL);
return 0;
case SNDCTL_DSP_RESET:
audio_devs[dev]->audio_mode = AM_NONE;
DMAbuf_reset(dev);
return 0;
case SNDCTL_DSP_GETFMTS:
val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
break;
case SNDCTL_DSP_SETFMT:
if (get_user(val, p))
return -EFAULT;
val = set_format(dev, val);
break;
case SNDCTL_DSP_GETISPACE:
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return 0;
if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
return -EBUSY;
return dma_ioctl(dev, cmd, arg);
case SNDCTL_DSP_GETOSPACE:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EPERM;
if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
return -EBUSY;
return dma_ioctl(dev, cmd, arg);
case SNDCTL_DSP_NONBLOCK:
file->f_flags |= O_NONBLOCK;
return 0;
case SNDCTL_DSP_GETCAPS:
val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */
if (audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode == OPEN_READWRITE)
val |= DSP_CAP_DUPLEX;
if (audio_devs[dev]->coproc)
val |= DSP_CAP_COPROC;
if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
val |= DSP_CAP_BATCH;
if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
val |= DSP_CAP_TRIGGER;
break;
case SOUND_PCM_WRITE_RATE:
if (get_user(val, p))
return -EFAULT;
val = audio_devs[dev]->d->set_speed(dev, val);
break;
case SOUND_PCM_READ_RATE:
val = audio_devs[dev]->d->set_speed(dev, 0);
break;
case SNDCTL_DSP_STEREO:
if (get_user(val, p))
return -EFAULT;
if (val > 1 || val < 0)
return -EINVAL;
val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
break;
case SOUND_PCM_WRITE_CHANNELS:
if (get_user(val, p))
return -EFAULT;
val = audio_devs[dev]->d->set_channels(dev, val);
break;
case SOUND_PCM_READ_CHANNELS:
val = audio_devs[dev]->d->set_channels(dev, 0);
break;
case SOUND_PCM_READ_BITS:
val = audio_devs[dev]->d->set_bits(dev, 0);
break;
case SNDCTL_DSP_SETDUPLEX:
if (audio_devs[dev]->open_mode != OPEN_READWRITE)
return -EPERM;
return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
case SNDCTL_DSP_PROFILE:
if (get_user(val, p))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
audio_devs[dev]->dmap_out->applic_profile = val;
if (audio_devs[dev]->open_mode & OPEN_READ)
audio_devs[dev]->dmap_in->applic_profile = val;
return 0;
case SNDCTL_DSP_GETODELAY:
dmap = audio_devs[dev]->dmap_out;
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (!(dmap->flags & DMA_ALLOC_DONE))
{
val=0;
break;
}
spin_lock_irqsave(&dmap->lock,flags);
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
if (count < dmap->fragment_size && dmap->qhead != 0)
count += dmap->bytes_in_use; /* Pointer wrap not handled yet */
count += dmap->byte_counter;
/* Substract current count from the number of bytes written by app */
count = dmap->user_counter - count;
if (count < 0)
count = 0;
spin_unlock_irqrestore(&dmap->lock,flags);
val = count;
break;
default:
return dma_ioctl(dev, cmd, arg);
}
return put_user(val, p);
}
void audio_init_devices(void)
{
/*
* NOTE! This routine could be called several times during boot.
*/
}
void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
{
/*
* This routine breaks the physical device buffers to logical ones.
*/
struct audio_operations *dsp_dev = audio_devs[dev];
unsigned i, n;
unsigned sr, nc, sz, bsz;
sr = dsp_dev->d->set_speed(dev, 0);
nc = dsp_dev->d->set_channels(dev, 0);
sz = dsp_dev->d->set_bits(dev, 0);
if (sz == 8)
dmap->neutral_byte = NEUTRAL8;
else
dmap->neutral_byte = NEUTRAL16;
if (sr < 1 || nc < 1 || sz < 1)
{
/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
sr = DSP_DEFAULT_SPEED;
nc = 1;
sz = 8;
}
sz = sr * nc * sz;
sz /= 8; /* #bits -> #bytes */
dmap->data_rate = sz;
if (!dmap->needs_reorg)
return;
dmap->needs_reorg = 0;
if (dmap->fragment_size == 0)
{
/* Compute the fragment size using the default algorithm */
/*
* Compute a buffer size for time not exceeding 1 second.
* Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
* of sound (using the current speed, sample size and #channels).
*/
bsz = dmap->buffsize;
while (bsz > sz)
bsz /= 2;
if (bsz == dmap->buffsize)
bsz /= 2; /* Needs at least 2 buffers */
/*
* Split the computed fragment to smaller parts. After 3.5a9
* the default subdivision is 4 which should give better
* results when recording.
*/
if (dmap->subdivision == 0) /* Not already set */
{
dmap->subdivision = 4; /* Init to the default value */
if ((bsz / dmap->subdivision) > 4096)
dmap->subdivision *= 2;
if ((bsz / dmap->subdivision) < 4096)
dmap->subdivision = 1;
}
bsz /= dmap->subdivision;
if (bsz < 16)
bsz = 16; /* Just a sanity check */
dmap->fragment_size = bsz;
}
else
{
/*
* The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
* the buffer size computation has already been done.
*/
if (dmap->fragment_size > (dmap->buffsize / 2))
dmap->fragment_size = (dmap->buffsize / 2);
bsz = dmap->fragment_size;
}
if (audio_devs[dev]->min_fragment)
if (bsz < (1 << audio_devs[dev]->min_fragment))
bsz = 1 << audio_devs[dev]->min_fragment;
if (audio_devs[dev]->max_fragment)
if (bsz > (1 << audio_devs[dev]->max_fragment))
bsz = 1 << audio_devs[dev]->max_fragment;
bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
#ifdef OS_DMA_ALIGN_CHECK
OS_DMA_ALIGN_CHECK(bsz);
#endif
n = dmap->buffsize / bsz;
if (n > MAX_SUB_BUFFERS)
n = MAX_SUB_BUFFERS;
if (n > dmap->max_fragments)
n = dmap->max_fragments;
if (n < 2)
{
n = 2;
bsz /= 2;
}
dmap->nbufs = n;
dmap->bytes_in_use = n * bsz;
dmap->fragment_size = bsz;
dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
dmap->bytes_in_use; /* Approximately one hour */
if (dmap->raw_buf)
{
memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
}
for (i = 0; i < dmap->nbufs; i++)
{
dmap->counts[i] = 0;
}
dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
}
static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
{
if (fact == 0)
{
fact = dmap->subdivision;
if (fact == 0)
fact = 1;
return fact;
}
if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */
return -EINVAL;
if (fact > MAX_REALTIME_FACTOR)
return -EINVAL;
if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
return -EINVAL;
dmap->subdivision = fact;
return fact;
}
static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
{
int bytes, count;
if (fact == 0)
return -EIO;
if (dmap->subdivision != 0 ||
dmap->fragment_size) /* Too late to change */
return -EINVAL;
bytes = fact & 0xffff;
count = (fact >> 16) & 0x7fff;
if (count == 0)
count = MAX_SUB_BUFFERS;
else if (count < MAX_SUB_BUFFERS)
count++;
if (bytes < 4 || bytes > 17) /* <16 || > 512k */
return -EINVAL;
if (count < 2)
return -EINVAL;
if (audio_devs[dev]->min_fragment > 0)
if (bytes < audio_devs[dev]->min_fragment)
bytes = audio_devs[dev]->min_fragment;
if (audio_devs[dev]->max_fragment > 0)
if (bytes > audio_devs[dev]->max_fragment)
bytes = audio_devs[dev]->max_fragment;
#ifdef OS_DMA_MINBITS
if (bytes < OS_DMA_MINBITS)
bytes = OS_DMA_MINBITS;
#endif
dmap->fragment_size = (1 << bytes);
dmap->max_fragments = count;
if (dmap->fragment_size > dmap->buffsize)
dmap->fragment_size = dmap->buffsize;
if (dmap->fragment_size == dmap->buffsize &&
audio_devs[dev]->flags & DMA_AUTOMODE)
dmap->fragment_size /= 2; /* Needs at least 2 buffers */
dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
return bytes | ((count - 1) << 16);
}
static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
{
struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
struct dma_buffparms *dmap;
audio_buf_info info;
count_info cinfo;
int fact, ret, changed, bits, count, err;
unsigned long flags;
switch (cmd)
{
case SNDCTL_DSP_SUBDIVIDE:
ret = 0;
if (get_user(fact, (int __user *)arg))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
ret = dma_subdivide(dev, dmap_out, fact);
if (ret < 0)
return ret;
if (audio_devs[dev]->open_mode != OPEN_WRITE ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
ret = dma_subdivide(dev, dmap_in, fact);
if (ret < 0)
return ret;
break;
case SNDCTL_DSP_GETISPACE:
case SNDCTL_DSP_GETOSPACE:
dmap = dmap_out;
if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
return -EINVAL;
if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
dmap = dmap_in;
if (dmap->mapping_flags & DMA_MAP_MAPPED)
return -EINVAL;
if (!(dmap->flags & DMA_ALLOC_DONE))
reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
info.fragstotal = dmap->nbufs;
if (cmd == SNDCTL_DSP_GETISPACE)
info.fragments = dmap->qlen;
else
{
if (!DMAbuf_space_in_queue(dev))
info.fragments = 0;
else
{
info.fragments = DMAbuf_space_in_queue(dev);
if (audio_devs[dev]->d->local_qlen)
{
int tmp = audio_devs[dev]->d->local_qlen(dev);
if (tmp && info.fragments)
tmp--; /*
* This buffer has been counted twice
*/
info.fragments -= tmp;
}
}
}
if (info.fragments < 0)
info.fragments = 0;
else if (info.fragments > dmap->nbufs)
info.fragments = dmap->nbufs;
info.fragsize = dmap->fragment_size;
info.bytes = info.fragments * dmap->fragment_size;
if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
info.bytes -= dmap->counts[dmap->qhead];
else
{
info.fragments = info.bytes / dmap->fragment_size;
info.bytes -= dmap->user_counter % dmap->fragment_size;
}
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
case SNDCTL_DSP_SETTRIGGER:
if (get_user(bits, (int __user *)arg))
return -EFAULT;
bits &= audio_devs[dev]->open_mode;
if (audio_devs[dev]->d->trigger == NULL)
return -EINVAL;
if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
(bits & PCM_ENABLE_OUTPUT))
return -EINVAL;
if (bits & PCM_ENABLE_INPUT)
{
spin_lock_irqsave(&dmap_in->lock,flags);
changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT;
if (changed && audio_devs[dev]->go)
{
reorganize_buffers(dev, dmap_in, 1);
if ((err = audio_devs[dev]->d->prepare_for_input(dev,
dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
spin_unlock_irqrestore(&dmap_in->lock,flags);
return -err;
}
dmap_in->dma_mode = DMODE_INPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
DMAbuf_activate_recording(dev, dmap_in);
} else
audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT;
spin_unlock_irqrestore(&dmap_in->lock,flags);
}
if (bits & PCM_ENABLE_OUTPUT)
{
spin_lock_irqsave(&dmap_out->lock,flags);
changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT;
if (changed &&
(dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
audio_devs[dev]->go)
{
if (!(dmap_out->flags & DMA_ALLOC_DONE))
reorganize_buffers(dev, dmap_out, 0);
dmap_out->dma_mode = DMODE_OUTPUT;
audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT;
dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
DMAbuf_launch_output(dev, dmap_out);
} else
audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT;
spin_unlock_irqrestore(&dmap_out->lock,flags);
}
#if 0
if (changed && audio_devs[dev]->d->trigger)
audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
#endif
/* Falls through... */
case SNDCTL_DSP_GETTRIGGER:
ret = audio_devs[dev]->enable_bits;
break;
case SNDCTL_DSP_SETSYNCRO:
if (!audio_devs[dev]->d->trigger)
return -EINVAL;
audio_devs[dev]->d->trigger(dev, 0);
audio_devs[dev]->go = 0;
return 0;
case SNDCTL_DSP_GETIPTR:
if (!(audio_devs[dev]->open_mode & OPEN_READ))
return -EINVAL;
spin_lock_irqsave(&dmap_in->lock,flags);
cinfo.bytes = dmap_in->byte_counter;
cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */
cinfo.blocks = dmap_in->qlen;
cinfo.bytes += cinfo.ptr;
if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
dmap_in->qlen = 0; /* Reset interrupt counter */
spin_unlock_irqrestore(&dmap_in->lock,flags);
if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETOPTR:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
spin_lock_irqsave(&dmap_out->lock,flags);
cinfo.bytes = dmap_out->byte_counter;
cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
cinfo.blocks = dmap_out->qlen;
cinfo.bytes += cinfo.ptr;
if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
dmap_out->qlen = 0; /* Reset interrupt counter */
spin_unlock_irqrestore(&dmap_out->lock,flags);
if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
return -EFAULT;
return 0;
case SNDCTL_DSP_GETODELAY:
if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
return -EINVAL;
if (!(dmap_out->flags & DMA_ALLOC_DONE))
{
ret=0;
break;
}
spin_lock_irqsave(&dmap_out->lock,flags);
/* Compute number of bytes that have been played */
count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
count += dmap_out->byte_counter;
/* Substract current count from the number of bytes written by app */
count = dmap_out->user_counter - count;
if (count < 0)
count = 0;
spin_unlock_irqrestore(&dmap_out->lock,flags);
ret = count;
break;
case SNDCTL_DSP_POST:
if (audio_devs[dev]->dmap_out->qlen > 0)
if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
return 0;
case SNDCTL_DSP_GETBLKSIZE:
dmap = dmap_out;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
if (audio_devs[dev]->open_mode == OPEN_READ ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
if (audio_devs[dev]->open_mode == OPEN_READ)
dmap = dmap_in;
ret = dmap->fragment_size;
break;
case SNDCTL_DSP_SETFRAGMENT:
ret = 0;
if (get_user(fact, (int __user *)arg))
return -EFAULT;
if (audio_devs[dev]->open_mode & OPEN_WRITE)
ret = dma_set_fragment(dev, dmap_out, fact);
if (ret < 0)
return ret;
if (audio_devs[dev]->open_mode == OPEN_READ ||
(audio_devs[dev]->flags & DMA_DUPLEX &&
audio_devs[dev]->open_mode & OPEN_READ))
ret = dma_set_fragment(dev, dmap_in, fact);
if (ret < 0)
return ret;
if (!arg) /* don't know what this is good for, but preserve old semantics */
return 0;
break;
default:
if (!audio_devs[dev]->d->ioctl)
return -EINVAL;
return audio_devs[dev]->d->ioctl(dev, cmd, arg);
}
return put_user(ret, (int __user *)arg);
}

16
sound/oss/audio_syms.c Normal file
View File

@@ -0,0 +1,16 @@
/*
* Exported symbols for audio driver.
*/
#include <linux/module.h>
char audio_syms_symbol;
#include "sound_config.h"
#include "sound_calls.h"
EXPORT_SYMBOL(DMAbuf_start_dma);
EXPORT_SYMBOL(DMAbuf_open_dma);
EXPORT_SYMBOL(DMAbuf_close_dma);
EXPORT_SYMBOL(DMAbuf_inputintr);
EXPORT_SYMBOL(DMAbuf_outputintr);

99
sound/oss/awe_hw.h Normal file
View File

@@ -0,0 +1,99 @@
/*
* sound/awe_hw.h
*
* Access routines and definitions for the low level driver for the
* Creative AWE32/SB32/AWE64 wave table synth.
* version 0.4.4; Jan. 4, 2000
*
* Copyright (C) 1996-2000 Takashi Iwai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef AWE_HW_H_DEF
#define AWE_HW_H_DEF
/*
* Emu-8000 control registers
* name(channel) reg, port
*/
#define awe_cmd_idx(reg,ch) (((reg)<< 5) | (ch))
#define Data0 0 /* 0x620: doubleword r/w */
#define Data1 1 /* 0xA20: doubleword r/w */
#define Data2 2 /* 0xA22: word r/w */
#define Data3 3 /* 0xE20: word r/w */
#define Pointer 4 /* 0xE22 register pointer r/w */
#define AWE_CPF(ch) awe_cmd_idx(0,ch), Data0 /* DW: current pitch and fractional address */
#define AWE_PTRX(ch) awe_cmd_idx(1,ch), Data0 /* DW: pitch target and reverb send */
#define AWE_CVCF(ch) awe_cmd_idx(2,ch), Data0 /* DW: current volume and filter cutoff */
#define AWE_VTFT(ch) awe_cmd_idx(3,ch), Data0 /* DW: volume and filter cutoff targets */
#define AWE_0080(ch) awe_cmd_idx(4,ch), Data0 /* DW: ?? */
#define AWE_00A0(ch) awe_cmd_idx(5,ch), Data0 /* DW: ?? */
#define AWE_PSST(ch) awe_cmd_idx(6,ch), Data0 /* DW: pan send and loop start address */
#define AWE_CSL(ch) awe_cmd_idx(7,ch), Data0 /* DW: chorus send and loop end address */
#define AWE_CCCA(ch) awe_cmd_idx(0,ch), Data1 /* DW: Q, control bits, and current address */
#define AWE_HWCF4 awe_cmd_idx(1,9), Data1 /* DW: config dw 4 */
#define AWE_HWCF5 awe_cmd_idx(1,10), Data1 /* DW: config dw 5 */
#define AWE_HWCF6 awe_cmd_idx(1,13), Data1 /* DW: config dw 6 */
#define AWE_HWCF7 awe_cmd_idx(1,14), Data1 /* DW: config dw 7? (not documented) */
#define AWE_SMALR awe_cmd_idx(1,20), Data1 /* DW: sound memory address for left read */
#define AWE_SMARR awe_cmd_idx(1,21), Data1 /* DW: for right read */
#define AWE_SMALW awe_cmd_idx(1,22), Data1 /* DW: sound memory address for left write */
#define AWE_SMARW awe_cmd_idx(1,23), Data1 /* DW: for right write */
#define AWE_SMLD awe_cmd_idx(1,26), Data1 /* W: sound memory left data */
#define AWE_SMRD awe_cmd_idx(1,26), Data2 /* W: right data */
#define AWE_WC awe_cmd_idx(1,27), Data2 /* W: sample counter */
#define AWE_WC_Cmd awe_cmd_idx(1,27)
#define AWE_WC_Port Data2
#define AWE_HWCF1 awe_cmd_idx(1,29), Data1 /* W: config w 1 */
#define AWE_HWCF2 awe_cmd_idx(1,30), Data1 /* W: config w 2 */
#define AWE_HWCF3 awe_cmd_idx(1,31), Data1 /* W: config w 3 */
#define AWE_INIT1(ch) awe_cmd_idx(2,ch), Data1 /* W: init array 1 */
#define AWE_INIT2(ch) awe_cmd_idx(2,ch), Data2 /* W: init array 2 */
#define AWE_INIT3(ch) awe_cmd_idx(3,ch), Data1 /* W: init array 3 */
#define AWE_INIT4(ch) awe_cmd_idx(3,ch), Data2 /* W: init array 4 */
#define AWE_ENVVOL(ch) awe_cmd_idx(4,ch), Data1 /* W: volume envelope delay */
#define AWE_DCYSUSV(ch) awe_cmd_idx(5,ch), Data1 /* W: volume envelope sustain and decay */
#define AWE_ENVVAL(ch) awe_cmd_idx(6,ch), Data1 /* W: modulation envelope delay */
#define AWE_DCYSUS(ch) awe_cmd_idx(7,ch), Data1 /* W: modulation envelope sustain and decay */
#define AWE_ATKHLDV(ch) awe_cmd_idx(4,ch), Data2 /* W: volume envelope attack and hold */
#define AWE_LFO1VAL(ch) awe_cmd_idx(5,ch), Data2 /* W: LFO#1 Delay */
#define AWE_ATKHLD(ch) awe_cmd_idx(6,ch), Data2 /* W: modulation envelope attack and hold */
#define AWE_LFO2VAL(ch) awe_cmd_idx(7,ch), Data2 /* W: LFO#2 Delay */
#define AWE_IP(ch) awe_cmd_idx(0,ch), Data3 /* W: initial pitch */
#define AWE_IFATN(ch) awe_cmd_idx(1,ch), Data3 /* W: initial filter cutoff and attenuation */
#define AWE_PEFE(ch) awe_cmd_idx(2,ch), Data3 /* W: pitch and filter envelope heights */
#define AWE_FMMOD(ch) awe_cmd_idx(3,ch), Data3 /* W: vibrato and filter modulation freq */
#define AWE_TREMFRQ(ch) awe_cmd_idx(4,ch), Data3 /* W: LFO#1 tremolo amount and freq */
#define AWE_FM2FRQ2(ch) awe_cmd_idx(5,ch), Data3 /* W: LFO#2 vibrato amount and freq */
/* used during detection (returns ROM version?; not documented in ADIP) */
#define AWE_U1 0xE0, Data3 /* (R)(W) used in initialization */
#define AWE_U2(ch) 0xC0+(ch), Data3 /* (W)(W) used in init envelope */
#define AWE_MAX_VOICES 32
#define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/
#define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */
#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */
#define AWE_DRAM_OFFSET 0x200000
#define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */
#endif

6147
sound/oss/awe_wave.c Normal file

File diff suppressed because it is too large Load Diff

77
sound/oss/awe_wave.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* sound/awe_config.h
*
* Configuration of AWE32/SB32/AWE64 wave table synth driver.
* version 0.4.4; Jan. 4, 2000
*
* Copyright (C) 1996-1998 Takashi Iwai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* chorus & reverb effects send for FM chip: from 0 to 0xff
* larger numbers often cause weird sounds.
*/
#define DEF_FM_CHORUS_DEPTH 0x10
#define DEF_FM_REVERB_DEPTH 0x10
/*
* other compile conditions
*/
/* initialize FM passthrough even without extended RAM */
#undef AWE_ALWAYS_INIT_FM
/* debug on */
#define AWE_DEBUG_ON
/* GUS compatible mode */
#define AWE_HAS_GUS_COMPATIBILITY
/* add MIDI emulation by wavetable */
#define CONFIG_AWE32_MIDIEMU
/* add mixer control of emu8000 equalizer */
#undef CONFIG_AWE32_MIXER
/* use new volume calculation method as default */
#define AWE_USE_NEW_VOLUME_CALC
/* check current volume target for searching empty voices */
#define AWE_CHECK_VTARGET
/* allow sample sharing */
#define AWE_ALLOW_SAMPLE_SHARING
/*
* AWE32 card configuration:
* uncomment the following lines *ONLY* when auto detection doesn't
* work properly on your machine.
*/
/*#define AWE_DEFAULT_BASE_ADDR 0x620*/ /* base port address */
/*#define AWE_DEFAULT_MEM_SIZE 512*/ /* kbytes */
/*
* AWE driver version number
*/
#define AWE_MAJOR_VERSION 0
#define AWE_MINOR_VERSION 4
#define AWE_TINY_VERSION 4
#define AWE_VERSION_NUMBER ((AWE_MAJOR_VERSION<<16)|(AWE_MINOR_VERSION<<8)|AWE_TINY_VERSION)
#define AWEDRV_VERSION "0.4.4"

39
sound/oss/bin2hex.c Normal file
View File

@@ -0,0 +1,39 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main( int argc, const char * argv [] )
{
const char * varname;
int i = 0;
int c;
int id = 0;
if(argv[1] && strcmp(argv[1],"-i")==0)
{
argv++;
argc--;
id=1;
}
if(argc==1)
{
fprintf(stderr, "bin2hex: [-i] firmware\n");
exit(1);
}
varname = argv[1];
printf( "/* automatically generated by bin2hex */\n" );
printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
while ( ( c = getchar( ) ) != EOF )
{
if ( i != 0 && i % 10 == 0 )
printf( "\n" );
printf( "0x%02lx,", c & 0xFFl );
i++;
}
printf( "};\nstatic int %sLen = %d;\n", varname, i );
return 0;
}

1136
sound/oss/btaudio.c Normal file

File diff suppressed because it is too large Load Diff

3378
sound/oss/cmpci.c Normal file

File diff suppressed because it is too large Load Diff

12
sound/oss/coproc.h Normal file
View File

@@ -0,0 +1,12 @@
/*
* Definitions for various on board processors on the sound cards. For
* example DSP processors.
*/
/*
* Coprocessor access types
*/
#define COPR_CUSTOM 0x0001 /* Custom applications */
#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */
#define COPR_PCM 0x0004 /* Digitized voice applications */
#define COPR_SYNTH 0x0008 /* Music synthesis */

520
sound/oss/cs4232.c Normal file
View File

@@ -0,0 +1,520 @@
/*
* Copyright (C) by Hannu Savolainen 1993-1997
*
* cs4232.c
*
* The low level driver for Crystal CS4232 based cards. The CS4232 is
* a PnP compatible chip which contains a CS4231A codec, SB emulation,
* a MPU401 compatible MIDI port, joystick and synthesizer and IDE CD-ROM
* interfaces. This is just a temporary driver until full PnP support
* gets implemented. Just the WSS codec, FM synth and the MIDI ports are
* supported. Other interfaces are left uninitialized.
*
* ifdef ...WAVEFRONT...
*
* Support is provided for initializing the WaveFront synth
* interface as well, which is logical device #4. Note that if
* you have a Tropez+ card, you probably don't need to setup
* the CS4232-supported MIDI interface, since it corresponds to
* the internal 26-pin header that's hard to access. Using this
* requires an additional IRQ, a resource none too plentiful in
* this environment. Just don't set module parameters mpuio and
* mpuirq, and the MIDI port will be left uninitialized. You can
* still use the ICS2115 hosted MIDI interface which corresponds
* to the 9-pin D connector on the back of the card.
*
* endif ...WAVEFRONT...
*
* Supported chips are:
* CS4232
* CS4236
* CS4236B
*
* Note: You will need a PnP config setup to initialise some CS4232 boards
* anyway.
*
* Changes
* John Rood Added Bose Sound System Support.
* Toshio Spoor
* Alan Cox Modularisation, Basic cleanups.
* Paul Barton-Davis Separated MPU configuration, added
* Tropez+ (WaveFront) support
* Christoph Hellwig Adapted to module_init/module_exit,
* simple cleanups
* Arnaldo C. de Melo got rid of attach_uart401
* Bartlomiej Zolnierkiewicz
* Added some __init/__initdata/__exit
* Marcus Meissner Added ISA PnP support.
*/
#include <linux/config.h>
#include <linux/pnp.h>
#include <linux/module.h>
#include <linux/init.h>
#include "sound_config.h"
#include "ad1848.h"
#include "mpu401.h"
#define KEY_PORT 0x279 /* Same as LPT1 status port */
#define CSN_NUM 0x99 /* Just a random number */
#define INDEX_ADDRESS 0x00 /* (R0) Index Address Register */
#define INDEX_DATA 0x01 /* (R1) Indexed Data Register */
#define PIN_CONTROL 0x0a /* (I10) Pin Control */
#define ENABLE_PINS 0xc0 /* XCTRL0/XCTRL1 enable */
static void CS_OUT(unsigned char a)
{
outb(a, KEY_PORT);
}
#define CS_OUT2(a, b) {CS_OUT(a);CS_OUT(b);}
#define CS_OUT3(a, b, c) {CS_OUT(a);CS_OUT(b);CS_OUT(c);}
static int __initdata bss = 0;
static int mpu_base, mpu_irq;
static int synth_base, synth_irq;
static int mpu_detected;
static int probe_cs4232_mpu(struct address_info *hw_config)
{
/*
* Just write down the config values.
*/
mpu_base = hw_config->io_base;
mpu_irq = hw_config->irq;
return 1;
}
static unsigned char crystal_key[] = /* A 32 byte magic key sequence */
{
0x96, 0x35, 0x9a, 0xcd, 0xe6, 0xf3, 0x79, 0xbc,
0x5e, 0xaf, 0x57, 0x2b, 0x15, 0x8a, 0xc5, 0xe2,
0xf1, 0xf8, 0x7c, 0x3e, 0x9f, 0x4f, 0x27, 0x13,
0x09, 0x84, 0x42, 0xa1, 0xd0, 0x68, 0x34, 0x1a
};
static void sleep(unsigned howlong)
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(howlong);
}
static void enable_xctrl(int baseio)
{
unsigned char regd;
/*
* Some IBM Aptiva's have the Bose Sound System. By default
* the Bose Amplifier is disabled. The amplifier will be
* activated, by setting the XCTRL0 and XCTRL1 bits.
* Volume of the monitor bose speakers/woofer, can then
* be set by changing the PCM volume.
*
*/
printk("cs4232: enabling Bose Sound System Amplifier.\n");
/* Switch to Pin Control Address */
regd = inb(baseio + INDEX_ADDRESS) & 0xe0;
outb(((unsigned char) (PIN_CONTROL | regd)), baseio + INDEX_ADDRESS );
/* Activate the XCTRL0 and XCTRL1 Pins */
regd = inb(baseio + INDEX_DATA);
outb(((unsigned char) (ENABLE_PINS | regd)), baseio + INDEX_DATA );
}
static int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured)
{
int i, n;
int base = hw_config->io_base, irq = hw_config->irq;
int dma1 = hw_config->dma, dma2 = hw_config->dma2;
struct resource *ports;
if (base == -1 || irq == -1 || dma1 == -1) {
printk(KERN_ERR "cs4232: dma, irq and io must be set.\n");
return 0;
}
/*
* Verify that the I/O port range is free.
*/
ports = request_region(base, 4, "ad1848");
if (!ports) {
printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base);
return 0;
}
if (ad1848_detect(ports, NULL, hw_config->osp)) {
goto got_it; /* The card is already active */
}
if (isapnp_configured) {
printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n");
goto fail;
}
/*
* This version of the driver doesn't use the PnP method when configuring
* the card but a simplified method defined by Crystal. This means that
* just one CS4232 compatible device can exist on the system. Also this
* method conflicts with possible PnP support in the OS. For this reason
* driver is just a temporary kludge.
*
* Also the Cirrus/Crystal method doesn't always work. Try ISA PnP first ;)
*/
/*
* Repeat initialization few times since it doesn't always succeed in
* first time.
*/
for (n = 0; n < 4; n++)
{
/*
* Wake up the card by sending a 32 byte Crystal key to the key port.
*/
for (i = 0; i < 32; i++)
CS_OUT(crystal_key[i]);
sleep(HZ / 10);
/*
* Now set the CSN (Card Select Number).
*/
CS_OUT2(0x06, CSN_NUM);
/*
* Then set some config bytes. First logical device 0
*/
CS_OUT2(0x15, 0x00); /* Select logical device 0 (WSS/SB/FM) */
CS_OUT3(0x47, (base >> 8) & 0xff, base & 0xff); /* WSS base */
if (check_region(0x388, 4)) /* Not free */
CS_OUT3(0x48, 0x00, 0x00) /* FM base off */
else
CS_OUT3(0x48, 0x03, 0x88); /* FM base 0x388 */
CS_OUT3(0x42, 0x00, 0x00); /* SB base off */
CS_OUT2(0x22, irq); /* SB+WSS IRQ */
CS_OUT2(0x2a, dma1); /* SB+WSS DMA */
if (dma2 != -1)
CS_OUT2(0x25, dma2) /* WSS DMA2 */
else
CS_OUT2(0x25, 4); /* No WSS DMA2 */
CS_OUT2(0x33, 0x01); /* Activate logical dev 0 */
sleep(HZ / 10);
/*
* Initialize logical device 3 (MPU)
*/
if (mpu_base != 0 && mpu_irq != 0)
{
CS_OUT2(0x15, 0x03); /* Select logical device 3 (MPU) */
CS_OUT3(0x47, (mpu_base >> 8) & 0xff, mpu_base & 0xff); /* MPU base */
CS_OUT2(0x22, mpu_irq); /* MPU IRQ */
CS_OUT2(0x33, 0x01); /* Activate logical dev 3 */
}
if(synth_base != 0)
{
CS_OUT2 (0x15, 0x04); /* logical device 4 (WaveFront) */
CS_OUT3 (0x47, (synth_base >> 8) & 0xff,
synth_base & 0xff); /* base */
CS_OUT2 (0x22, synth_irq); /* IRQ */
CS_OUT2 (0x33, 0x01); /* Activate logical dev 4 */
}
/*
* Finally activate the chip
*/
CS_OUT(0x79);
sleep(HZ / 5);
/*
* Then try to detect the codec part of the chip
*/
if (ad1848_detect(ports, NULL, hw_config->osp))
goto got_it;
sleep(HZ);
}
fail:
release_region(base, 4);
return 0;
got_it:
if (dma2 == -1)
dma2 = dma1;
hw_config->slots[0] = ad1848_init("Crystal audio controller", ports,
irq,
dma1, /* Playback DMA */
dma2, /* Capture DMA */
0,
hw_config->osp,
THIS_MODULE);
if (hw_config->slots[0] != -1 &&
audio_devs[hw_config->slots[0]]->mixer_dev!=-1)
{
/* Assume the mixer map is as suggested in the CS4232 databook */
AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE);
AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* FM synth */
}
if (mpu_base != 0 && mpu_irq != 0)
{
static struct address_info hw_config2 = {
0
}; /* Ensure it's initialized */
hw_config2.io_base = mpu_base;
hw_config2.irq = mpu_irq;
hw_config2.dma = -1;
hw_config2.dma2 = -1;
hw_config2.always_detect = 0;
hw_config2.name = NULL;
hw_config2.driver_use_1 = 0;
hw_config2.driver_use_2 = 0;
hw_config2.card_subtype = 0;
if (probe_uart401(&hw_config2, THIS_MODULE))
{
mpu_detected = 1;
}
else
{
mpu_base = mpu_irq = 0;
}
hw_config->slots[1] = hw_config2.slots[1];
}
if (bss)
enable_xctrl(base);
return 1;
}
static void __devexit unload_cs4232(struct address_info *hw_config)
{
int base = hw_config->io_base, irq = hw_config->irq;
int dma1 = hw_config->dma, dma2 = hw_config->dma2;
if (dma2 == -1)
dma2 = dma1;
ad1848_unload(base,
irq,
dma1, /* Playback DMA */
dma2, /* Capture DMA */
0);
sound_unload_audiodev(hw_config->slots[0]);
if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
{
static struct address_info hw_config2 =
{
0
}; /* Ensure it's initialized */
hw_config2.io_base = mpu_base;
hw_config2.irq = mpu_irq;
hw_config2.dma = -1;
hw_config2.dma2 = -1;
hw_config2.always_detect = 0;
hw_config2.name = NULL;
hw_config2.driver_use_1 = 0;
hw_config2.driver_use_2 = 0;
hw_config2.card_subtype = 0;
hw_config2.slots[1] = hw_config->slots[1];
unload_uart401(&hw_config2);
}
}
static struct address_info cfg;
static struct address_info cfg_mpu;
static int __initdata io = -1;
static int __initdata irq = -1;
static int __initdata dma = -1;
static int __initdata dma2 = -1;
static int __initdata mpuio = -1;
static int __initdata mpuirq = -1;
static int __initdata synthio = -1;
static int __initdata synthirq = -1;
static int __initdata isapnp = 1;
MODULE_DESCRIPTION("CS4232 based soundcard driver");
MODULE_AUTHOR("Hannu Savolainen, Paul Barton-Davis");
MODULE_LICENSE("GPL");
module_param(io, int, 0);
MODULE_PARM_DESC(io,"base I/O port for AD1848");
module_param(irq, int, 0);
MODULE_PARM_DESC(irq,"IRQ for AD1848 chip");
module_param(dma, int, 0);
MODULE_PARM_DESC(dma,"8 bit DMA for AD1848 chip");
module_param(dma2, int, 0);
MODULE_PARM_DESC(dma2,"16 bit DMA for AD1848 chip");
module_param(mpuio, int, 0);
MODULE_PARM_DESC(mpuio,"MPU 401 base address");
module_param(mpuirq, int, 0);
MODULE_PARM_DESC(mpuirq,"MPU 401 IRQ");
module_param(synthio, int, 0);
MODULE_PARM_DESC(synthio,"Maui WaveTable base I/O port");
module_param(synthirq, int, 0);
MODULE_PARM_DESC(synthirq,"Maui WaveTable IRQ");
module_param(isapnp, bool, 0);
MODULE_PARM_DESC(isapnp,"Enable ISAPnP probing (default 1)");
module_param(bss, bool, 0);
MODULE_PARM_DESC(bss,"Enable Bose Sound System Support (default 0)");
/*
* Install a CS4232 based card. Need to have ad1848 and mpu401
* loaded ready.
*/
/* All cs4232 based cards have the main ad1848 card either as CSC0000 or
* CSC0100. */
static const struct pnp_device_id cs4232_pnp_table[] = {
{ .id = "CSC0100", .driver_data = 0 },
{ .id = "CSC0000", .driver_data = 0 },
/* Guillemot Turtlebeach something appears to be cs4232 compatible
* (untested) */
{ .id = "GIM0100", .driver_data = 0 },
{ .id = ""}
};
MODULE_DEVICE_TABLE(pnp, cs4232_pnp_table);
static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
struct address_info *isapnpcfg;
isapnpcfg=(struct address_info*)kmalloc(sizeof(*isapnpcfg),GFP_KERNEL);
if (!isapnpcfg)
return -ENOMEM;
isapnpcfg->irq = pnp_irq(dev, 0);
isapnpcfg->dma = pnp_dma(dev, 0);
isapnpcfg->dma2 = pnp_dma(dev, 1);
isapnpcfg->io_base = pnp_port_start(dev, 0);
if (probe_cs4232(isapnpcfg,TRUE) == 0) {
printk(KERN_ERR "cs4232: ISA PnP card found, but not detected?\n");
kfree(isapnpcfg);
return -ENODEV;
}
pnp_set_drvdata(dev,isapnpcfg);
return 0;
}
static void __devexit cs4232_pnp_remove(struct pnp_dev *dev)
{
struct address_info *cfg = pnp_get_drvdata(dev);
if (cfg) {
unload_cs4232(cfg);
kfree(cfg);
}
}
static struct pnp_driver cs4232_driver = {
.name = "cs4232",
.id_table = cs4232_pnp_table,
.probe = cs4232_pnp_probe,
.remove = __devexit_p(cs4232_pnp_remove),
};
static int __init init_cs4232(void)
{
#ifdef CONFIG_SOUND_WAVEFRONT_MODULE
if(synthio == -1)
printk(KERN_INFO "cs4232: set synthio and synthirq to use the wavefront facilities.\n");
else {
synth_base = synthio;
synth_irq = synthirq;
}
#else
if(synthio != -1)
printk(KERN_WARNING "cs4232: wavefront support not enabled in this driver.\n");
#endif
cfg.irq = -1;
if (isapnp &&
(pnp_register_driver(&cs4232_driver) > 0)
)
return 0;
if(io==-1||irq==-1||dma==-1)
{
printk(KERN_ERR "cs4232: Must set io, irq and dma.\n");
return -ENODEV;
}
cfg.io_base = io;
cfg.irq = irq;
cfg.dma = dma;
cfg.dma2 = dma2;
cfg_mpu.io_base = -1;
cfg_mpu.irq = -1;
if (mpuio != -1 && mpuirq != -1) {
cfg_mpu.io_base = mpuio;
cfg_mpu.irq = mpuirq;
probe_cs4232_mpu(&cfg_mpu); /* Bug always returns 0 not OK -- AC */
}
if (probe_cs4232(&cfg,FALSE) == 0)
return -ENODEV;
return 0;
}
static void __exit cleanup_cs4232(void)
{
pnp_unregister_driver(&cs4232_driver);
if (cfg.irq != -1)
unload_cs4232(&cfg); /* Unloads global MPU as well, if needed */
}
module_init(init_cs4232);
module_exit(cleanup_cs4232);
#ifndef MODULE
static int __init setup_cs4232(char *str)
{
/* io, irq, dma, dma2 mpuio, mpuirq*/
int ints[7];
/* If we have isapnp cards, no need for options */
if (pnp_register_driver(&cs4232_driver) > 0)
return 1;
str = get_options(str, ARRAY_SIZE(ints), ints);
io = ints[1];
irq = ints[2];
dma = ints[3];
dma2 = ints[4];
mpuio = ints[5];
mpuirq = ints[6];
return 1;
}
__setup("cs4232=", setup_cs4232);
#endif

View File

@@ -0,0 +1,6 @@
# Makefile for Cirrus Logic-Crystal CS4281
#
obj-$(CONFIG_SOUND_CS4281) += cs4281.o
cs4281-objs += cs4281m.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
*
* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (audio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 12/20/00 trw - new file.
*
*******************************************************************************/
#include <linux/spinlock.h>
static int cs4281_resume_null(struct pci_dev *pcidev) { return 0; }
static int cs4281_suspend_null(struct pci_dev *pcidev, pm_message_t state) { return 0; }
#define free_dmabuf(state, dmabuf) \
pci_free_consistent(state->pcidev, \
PAGE_SIZE << (dmabuf)->buforder, \
(dmabuf)->rawbuf, (dmabuf)->dmaaddr);
#define free_dmabuf2(state, dmabuf) \
pci_free_consistent((state)->pcidev, \
PAGE_SIZE << (state)->buforder_tmpbuff, \
(state)->tmpbuff, (state)->dmaaddr_tmpbuff);
#define cs4x_pgoff(vma) ((vma)->vm_pgoff)

4505
sound/oss/cs4281/cs4281m.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
/*******************************************************************************
*
* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (audio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 12/22/00 trw - new file.
*
*******************************************************************************/
#ifndef NOT_CS4281_PM
#include <linux/pm.h>
#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
#define cs_pm_unregister_all(a) pm_unregister_all((a));
static int cs4281_suspend(struct cs4281_state *s);
static int cs4281_resume(struct cs4281_state *s);
/*
* for now (12/22/00) only enable the pm_register PM support.
* allow these table entries to be null.
#define CS4281_SUSPEND_TBL cs4281_suspend_tbl
#define CS4281_RESUME_TBL cs4281_resume_tbl
*/
#define CS4281_SUSPEND_TBL cs4281_suspend_null
#define CS4281_RESUME_TBL cs4281_resume_null
static int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
{
struct cs4281_state *state;
CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
"cs4281: cs4281_pm_callback dev=%p rqst=0x%x state=%p\n",
dev,(unsigned)rqst,data));
state = (struct cs4281_state *) dev->data;
if (state) {
switch(rqst) {
case PM_SUSPEND:
CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
"cs4281: PM suspend request\n"));
if(cs4281_suspend(state))
{
CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
"cs4281: PM suspend request refused\n"));
return 1;
}
break;
case PM_RESUME:
CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
"cs4281: PM resume request\n"));
if(cs4281_resume(state))
{
CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
"cs4281: PM resume request refused\n"));
return 1;
}
break;
}
}
return 0;
}
#else /* CS4281_PM */
#define CS4281_SUSPEND_TBL cs4281_suspend_null
#define CS4281_RESUME_TBL cs4281_resume_null
#endif /* CS4281_PM */

View File

@@ -0,0 +1,74 @@
#ifndef NOT_CS4281_PM
/*******************************************************************************
*
* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (audio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 12/22/00 trw - new file.
*
*******************************************************************************/
/* general pm definitions */
#define CS4281_AC97_HIGHESTREGTORESTORE 0x26
#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1)
/* pipeline definitions */
#define CS4281_NUMBER_OF_PIPELINES 4
#define CS4281_PIPELINE_VALID 0x0001
#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000
#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001
/* PM state defintions */
#define CS4281_PM_NOT_REGISTERED 0x1000
#define CS4281_PM_IDLE 0x0001
#define CS4281_PM_SUSPENDING 0x0002
#define CS4281_PM_SUSPENDED 0x0004
#define CS4281_PM_RESUMING 0x0008
#define CS4281_PM_RESUMED 0x0010
struct cs4281_pm {
unsigned long flags;
u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue;
u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR;
u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save;
u32 u32SSPM_BITS;
u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS];
u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono;
u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose;
u32 u32hwptr_playback,u32hwptr_capture;
};
struct cs4281_pipeline {
unsigned flags;
unsigned number;
u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue;
u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress;
u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress;
u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save;
u32 u32DCCn_Save,u32DCAn_Save;
/*
* technically, these are fifo variables, but just map the
* first fifo with the first pipeline and then use the fifo
* variables inside of the pipeline struct.
*/
u32 u32FCRn_Save,u32FSICn_Save;
u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress;
u32 u32FPDRnValue,u32FPDRnAddress;
};
#endif

1691
sound/oss/cs461x.h Normal file

File diff suppressed because it is too large Load Diff

322
sound/oss/cs461x_image.h Normal file
View File

@@ -0,0 +1,322 @@
/****************************************************************************
* "CWCIMAGE.H"-- For CS46XX. Ver 1.04
* Copyright 1998-2001 (c) Cirrus Logic Corp.
* Version 1.04
****************************************************************************
*/
#ifndef __CS_IMAGE_H
#define __CS_IMAGE_H
#define CLEAR__COUNT 3
#define FILL__COUNT 4
#define BA1__DWORD_SIZE 13*1024+512
static struct
{
unsigned BA1__DestByteOffset;
unsigned BA1__SourceSize;
} ClrStat[CLEAR__COUNT] ={ {0x00000000, 0x00003000 },
{0x00010000, 0x00003800 },
{0x00020000, 0x00007000 } };
static u32 FillArray1[]={
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000163,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00200040,0x00008010,0x00000000,
0x00000000,0x80000001,0x00000001,0x00060000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00900080,0x00000173,0x00000000,
0x00000000,0x00000010,0x00800000,0x00900000,
0xf2c0000f,0x00000200,0x00000000,0x00010600,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000163,0x330300c2,
0x06000000,0x00000000,0x80008000,0x80008000,
0x3fc0000f,0x00000301,0x00010400,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00b00000,0x00d0806d,0x330480c3,
0x04800000,0x00000001,0x00800001,0x0000ffff,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x066a0600,0x06350070,0x0000929d,0x929d929d,
0x00000000,0x0000735a,0x00000600,0x00000000,
0x929d735a,0x00000000,0x00010000,0x735a735a,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x0000804f,0x000000c3,
0x05000000,0x00a00010,0x00000000,0x80008000,
0x00000000,0x00000000,0x00000700,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000080,0x00a00000,0x0000809a,0x000000c2,
0x07400000,0x00000000,0x80008000,0xffffffff,
0x00c80028,0x00005555,0x00000000,0x000107a0,
0x00c80028,0x000000c2,0x06800000,0x00000000,
0x06e00080,0x00300000,0x000080bb,0x000000c9,
0x07a00000,0x04000000,0x80008000,0xffffffff,
0x00c80028,0x00005555,0x00000000,0x00000780,
0x00c80028,0x000000c5,0xff800000,0x00000000,
0x00640080,0x00c00000,0x00008197,0x000000c9,
0x07800000,0x04000000,0x80008000,0xffffffff,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x0000805e,0x000000c1,
0x00000000,0x00800000,0x80008000,0x80008000,
0x00020000,0x0000ffff,0x00000000,0x00000000};
static u32 FillArray2[]={
0x929d0600,0x929d929d,0x929d929d,0x929d0000,
0x929d929d,0x929d929d,0x929d929d,0x929d929d,
0x929d929d,0x00100635,0x060b013f,0x00000004,
0x00000001,0x007a0002,0x00000000,0x066e0610,
0x0105929d,0x929d929d,0x929d929d,0x929d929d,
0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
0x00000000,0x929d929d,0x929d929d,0x929d929d,
0x929d929d,0x929d929d,0x929d929d,0x929d929d,
0x929d929d,0x929d929d,0x00000000,0x06400136,
0x0000270f,0x00010000,0x007a0000,0x00000000,
0x068e0645,0x0105929d,0x929d929d,0x929d929d,
0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
0x735a0100,0x00000000,0x00000000,0x00000000};
static u32 FillArray3[]={
0x00000000,0x00000000,0x00000000,0x00010004};
static u32 FillArray4[]={
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00001705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00009705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00011705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00019705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00021705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00029705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00031705,0x00001400,0x000a411e,0x00001003,
0x00040730,0x00001002,0x000f619e,0x00001003,
0x00039705,0x00001400,0x000a411e,0x00001003,
0x000fe19e,0x00001003,0x0009c730,0x00001003,
0x0008e19c,0x00001003,0x000083c1,0x00093040,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00009705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00011705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00019705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00021705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00029705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00031705,0x00001400,0x000a211e,0x00001003,
0x00098730,0x00001002,0x000ee19e,0x00001003,
0x00039705,0x00001400,0x000a211e,0x00001003,
0x0000a730,0x00001008,0x000e2730,0x00001002,
0x0000a731,0x00001002,0x0000a731,0x00001002,
0x0000a731,0x00001002,0x0000a731,0x00001002,
0x0000a731,0x00001002,0x0000a731,0x00001002,
0x00000000,0x00000000,0x000f619c,0x00001003,
0x0007f801,0x000c0000,0x00000037,0x00001000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x000c0000,0x00000000,0x00000000,
0x0000373c,0x00001000,0x00000000,0x00000000,
0x000ee19c,0x00001003,0x0007f801,0x000c0000,
0x00000037,0x00001000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x0000273c,0x00001000,
0x00000033,0x00001000,0x000e679e,0x00001003,
0x00007705,0x00001400,0x000ac71e,0x00001003,
0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
0x00000037,0x00001000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x0000a730,0x00001003,
0x00000033,0x00001000,0x0007f801,0x000c0000,
0x00000037,0x00001000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x000c0000,
0x00000032,0x00001000,0x0000273d,0x00001000,
0x0004a730,0x00001003,0x00000f41,0x00097140,
0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
0x00000000,0x00000000,0x0001bf05,0x0003fc40,
0x00002725,0x000aa400,0x00013705,0x00093a00,
0x0000002e,0x0009d6c0,0x00038630,0x00001004,
0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
0x00000000,0x000c70e0,0x0007d182,0x0002c640,
0x00000630,0x00001004,0x000799b8,0x0002c6c0,
0x00031705,0x00092240,0x00039f05,0x000932c0,
0x0003520a,0x00000000,0x00040731,0x0000100b,
0x00010705,0x000b20c0,0x00000000,0x000eba44,
0x00032108,0x000c60c4,0x00065208,0x000c2917,
0x000406b0,0x00001007,0x00012f05,0x00036880,
0x0002818e,0x000c0000,0x0004410a,0x00000000,
0x00040630,0x00001007,0x00029705,0x000c0000,
0x00000000,0x00000000,0x00003fc1,0x0003fc40,
0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
0x000037c1,0x00000000,0x00003fc1,0x000991c0,
0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
0x000683ad,0x00095241,0x00020f05,0x000991c1,
0x00000000,0x00000000,0x00086f88,0x00001000,
0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
0x0009de81,0x000bd300,0x0009d601,0x000b1700,
0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
0x000a1681,0x000b97c0,0x00021601,0x00002500,
0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
0x00021681,0x00002d00,0x00020f81,0x000bd800,
0x000a0701,0x000b5bc0,0x00021601,0x00003500,
0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
0x00021681,0x00003d00,0x00020f81,0x000b1d00,
0x000a0701,0x000b1fc0,0x00021601,0x00020500,
0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
0x00021681,0x00020d00,0x00020f81,0x000bde80,
0x000a0701,0x000bdfc0,0x00021601,0x00021500,
0x00020f81,0x000b9341,0x00020701,0x000b53c1,
0x00021681,0x00021d00,0x000a0f81,0x000d0380,
0x0000b601,0x000b15c0,0x00007b01,0x00000000,
0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
0x0007e48a,0x00000000,0x00011f05,0x00084080,
0x00000000,0x00000000,0x00001705,0x000b3540,
0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
0x00055488,0x00000000,0x0000d482,0x0003fc40,
0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
0x000c86b0,0x00001007,0x00008281,0x000bb240,
0x0000b801,0x000b7140,0x00007888,0x00000000,
0x0000073c,0x00001000,0x0007f188,0x000c0000,
0x00000000,0x00000000,0x00055288,0x000c555c,
0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
0x0000fa88,0x00000000,0x00000032,0x00001000,
0x0000073d,0x00001000,0x0007f188,0x000c0000,
0x00000000,0x00000000,0x0008c01c,0x00001003,
0x00002705,0x00001008,0x0008b201,0x000c1392,
0x0000ba01,0x00000000,0x00008731,0x00001400,
0x0004c108,0x000fe0c4,0x00057488,0x00000000,
0x000a6388,0x00001001,0x0008b334,0x000bc141,
0x0003020e,0x00000000,0x000886b0,0x00001008,
0x00003625,0x000c5dfa,0x000a638a,0x00001001,
0x0008020e,0x00001002,0x0008a6b0,0x00001008,
0x0007f301,0x00000000,0x00000000,0x00000000,
0x00002725,0x000a8c40,0x000000ae,0x00000000,
0x000d8630,0x00001008,0x00000000,0x000c74e0,
0x0007d182,0x0002d640,0x000a8630,0x00001008,
0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
0x0007420a,0x000c0000,0x00062208,0x000c4117,
0x00070630,0x00001009,0x00000000,0x000c0000,
0x0001022e,0x00000000,0x0003a630,0x00001009,
0x00000000,0x000c0000,0x00000036,0x00001000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x0002a730,0x00001008,0x0007f801,0x000c0000,
0x00000037,0x00001000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x0002a730,0x00001008,
0x00000033,0x00001000,0x0002a705,0x00001008,
0x00007a01,0x000c0000,0x000e6288,0x000d550a,
0x0006428a,0x00000000,0x00060730,0x0000100a,
0x00000000,0x000c0000,0x00000000,0x00000000,
0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
0x00057488,0x00000000,0x00033b94,0x00081140,
0x000183ae,0x00000000,0x000786b0,0x0000100b,
0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
0x00042731,0x00001003,0x0007aab0,0x00034880,
0x00048fb0,0x0000100a,0x00057488,0x00000000,
0x00033b94,0x00081140,0x000183ae,0x00000000,
0x000806b0,0x0000100b,0x00022f05,0x00000000,
0x00007401,0x00091140,0x00048f05,0x000951c0,
0x00042731,0x00001003,0x0000473d,0x00001000,
0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
0x000fe19e,0x00001003,0x00000000,0x00000000,
0x0008e19c,0x00001003,0x000083c1,0x00093040,
0x00000f41,0x00097140,0x0000a841,0x0009b240,
0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
0x00055208,0x00000000,0x00010705,0x000a2880,
0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
0x00065308,0x000c2997,0x000d86b0,0x0000100a,
0x0004410a,0x000d40c7,0x00000000,0x00000000,
0x00080730,0x00001004,0x00056f0a,0x000ea105,
0x00000000,0x00000000,0x0000473d,0x00001000,
0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
0x0000273d,0x00001000,0x00000000,0x000eba44,
0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
0x00000734,0x00001000,0x00010705,0x000a6880,
0x00006a88,0x000c75c4,0x00000000,0x000e5084,
0x00000000,0x000eba44,0x00087401,0x000e4782,
0x00000734,0x00001000,0x00010705,0x000a6880,
0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
0x0007e721,0x000bed40,0x00005f25,0x000badc0,
0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
0x00033217,0x00003ec0,0x00065590,0x000b8e40,
0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
0x000283a0,0x0000100c,0x000ee388,0x00042970,
0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
0x00078898,0x00001000,0x00038894,0x00000032,
0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
0x00041705,0x0009ed40,0x00058730,0x00001400,
0x000d7488,0x000c3a00,0x00048f05,0x00000000};
static struct
{ u32 Offset;
u32 Size;
u32 *pFill;
} FillStat[FILL__COUNT] = {
{0x00000000, sizeof(FillArray1), FillArray1},
{0x00001800, sizeof(FillArray2), FillArray2},
{0x000137f0, sizeof(FillArray3), FillArray3},
{0x00020000, sizeof(FillArray4), FillArray4}
};
#endif

5794
sound/oss/cs46xx.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
/*******************************************************************************
*
* "cs46xx_wrapper.c" -- Cirrus Logic-Crystal CS46XX linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (pcaudio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 01/11/2001 trw - new file from cs4281 wrapper code.
*
*******************************************************************************/
#ifndef __CS46XX_WRAPPER24_H
#define __CS46XX_WRAPPER24_H
#include <linux/spinlock.h>
#define CS_OWNER .owner =
#define CS_THIS_MODULE THIS_MODULE,
static inline void cs46xx_null(struct pci_dev *pcidev) { return; }
#define cs4x_mem_map_reserve(page) SetPageReserved(page)
#define cs4x_mem_map_unreserve(page) ClearPageReserved(page)
#define free_dmabuf(card, dmabuf) \
pci_free_consistent((card)->pci_dev, \
PAGE_SIZE << (dmabuf)->buforder, \
(dmabuf)->rawbuf, (dmabuf)->dmaaddr);
#define free_dmabuf2(card, dmabuf) \
pci_free_consistent((card)->pci_dev, \
PAGE_SIZE << (dmabuf)->buforder_tmpbuff, \
(dmabuf)->tmpbuff, (dmabuf)->dmaaddr_tmpbuff);
#define cs4x_pgoff(vma) ((vma)->vm_pgoff)
#define RSRCISIOREGION(dev,num) ((dev)->resource[(num)].start != 0 && \
((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
#define RSRCISMEMORYREGION(dev,num) ((dev)->resource[(num)].start != 0 && \
((dev)->resource[(num)].flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY)
#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
#define PCI_GET_DRIVER_DATA pci_get_drvdata
#define PCI_SET_DRIVER_DATA pci_set_drvdata
#define PCI_SET_DMA_MASK(pcidev,mask) pcidev->dma_mask = mask
#endif

52
sound/oss/cs46xxpm-24.h Normal file
View File

@@ -0,0 +1,52 @@
/*******************************************************************************
*
* "cs46xxpm-24.h" -- Cirrus Logic-Crystal CS46XX linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (pcaudio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 12/22/00 trw - new file.
*
*******************************************************************************/
#ifndef __CS46XXPM24_H
#define __CS46XXPM24_H
#include <linux/pm.h>
#include "cs46xxpm.h"
#define CS46XX_ACPI_SUPPORT 1
#ifdef CS46XX_ACPI_SUPPORT
/*
* for now (12/22/00) only enable the pm_register PM support.
* allow these table entries to be null.
*/
static int cs46xx_suspend_tbl(struct pci_dev *pcidev, pm_message_t state);
static int cs46xx_resume_tbl(struct pci_dev *pcidev);
#define cs_pm_register(a, b, c) NULL
#define cs_pm_unregister_all(a)
#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl
#define CS46XX_RESUME_TBL cs46xx_resume_tbl
#else
#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
#define cs_pm_unregister_all(a) pm_unregister_all((a));
#define CS46XX_SUSPEND_TBL cs46xx_null
#define CS46XX_RESUME_TBL cs46xx_null
#endif
#endif

70
sound/oss/cs46xxpm.h Normal file
View File

@@ -0,0 +1,70 @@
/*******************************************************************************
*
* "cs46xxpm.h" -- Cirrus Logic-Crystal CS46XX linux audio driver.
*
* Copyright (C) 2000,2001 Cirrus Logic Corp.
* -- tom woller (twoller@crystal.cirrus.com) or
* (pcaudio@crystal.cirrus.com).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* 12/22/00 trw - new file.
*
*******************************************************************************/
#ifndef __CS46XXPM_H
#define __CS46XXPM_H
#define CS46XX_AC97_HIGHESTREGTORESTORE 0x26
#define CS46XX_AC97_NUMBER_RESTORE_REGS (CS46XX_AC97_HIGHESTREGTORESTORE/2-1)
/* PM state defintions */
#define CS46XX_PM_NOT_REGISTERED 0x1000
#define CS46XX_PM_IDLE 0x0001
#define CS46XX_PM_SUSPENDING 0x0002
#define CS46XX_PM_SUSPENDED 0x0004
#define CS46XX_PM_RESUMING 0x0008
#define CS46XX_PM_RESUMED 0x0010
#define CS_POWER_DAC 0x0001
#define CS_POWER_ADC 0x0002
#define CS_POWER_MIXVON 0x0004
#define CS_POWER_MIXVOFF 0x0008
#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */
#define CS_AC97_POWER_CONTROL_ADC 0x0100
#define CS_AC97_POWER_CONTROL_DAC 0x0200
#define CS_AC97_POWER_CONTROL_MIXVON 0x0400
#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800
#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001
#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002
#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004
#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008
struct cs46xx_pm {
unsigned long flags;
u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue;
u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR;
u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save;
u32 u32SSPM_BITS;
u32 ac97[CS46XX_AC97_NUMBER_RESTORE_REGS];
u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono;
u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose;
u32 u32hwptr_playback,u32hwptr_capture;
unsigned dmabuf_swptr_play;
int dmabuf_count_play;
unsigned dmabuf_swptr_capture;
int dmabuf_count_capture;
};
#endif

214
sound/oss/dev_table.c Normal file
View File

@@ -0,0 +1,214 @@
/*
* sound/dev_table.c
*
* Device call tables.
*
*
* 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/init.h>
#define _DEV_TABLE_C_
#include "sound_config.h"
static int sound_alloc_audiodev(void);
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
void *devc, int dma1, int dma2)
{
struct audio_driver *d;
struct audio_operations *op;
int num;
if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
return -(EINVAL);
}
num = sound_alloc_audiodev();
if (num == -1) {
printk(KERN_ERR "sound: Too many audio drivers\n");
return -(EBUSY);
}
d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
if (sound_nblocks < 1024)
sound_nblocks++;
op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations)));
if (sound_nblocks < 1024)
sound_nblocks++;
if (d == NULL || op == NULL) {
printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
sound_unload_audiodev(num);
return -(ENOMEM);
}
memset((char *) op, 0, sizeof(struct audio_operations));
init_waitqueue_head(&op->in_sleeper);
init_waitqueue_head(&op->out_sleeper);
init_waitqueue_head(&op->poll_sleeper);
if (driver_size < sizeof(struct audio_driver))
memset((char *) d, 0, sizeof(struct audio_driver));
memcpy((char *) d, (char *) driver, driver_size);
op->d = d;
strlcpy(op->name, name, sizeof(op->name));
op->flags = flags;
op->format_mask = format_mask;
op->devc = devc;
/*
* Hardcoded defaults
*/
audio_devs[num] = op;
DMAbuf_init(num, dma1, dma2);
audio_init_devices();
return num;
}
int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
int driver_size, void *devc)
{
struct mixer_operations *op;
int n = sound_alloc_mixerdev();
if (n == -1) {
printk(KERN_ERR "Sound: Too many mixer drivers\n");
return -EBUSY;
}
if (vers != MIXER_DRIVER_VERSION ||
driver_size > sizeof(struct mixer_operations)) {
printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
return -EINVAL;
}
/* FIXME: This leaks a mixer_operations struct every time its called
until you unload sound! */
op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations)));
if (sound_nblocks < 1024)
sound_nblocks++;
if (op == NULL) {
printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
return -ENOMEM;
}
memset((char *) op, 0, sizeof(struct mixer_operations));
memcpy((char *) op, (char *) driver, driver_size);
strlcpy(op->name, name, sizeof(op->name));
op->devc = devc;
mixer_devs[n] = op;
return n;
}
void sound_unload_audiodev(int dev)
{
if (dev != -1) {
DMAbuf_deinit(dev);
audio_devs[dev] = NULL;
unregister_sound_dsp((dev<<4)+3);
}
}
static int sound_alloc_audiodev(void)
{
int i = register_sound_dsp(&oss_sound_fops, -1);
if(i==-1)
return i;
i>>=4;
if(i>=num_audiodevs)
num_audiodevs = i + 1;
return i;
}
int sound_alloc_mididev(void)
{
int i = register_sound_midi(&oss_sound_fops, -1);
if(i==-1)
return i;
i>>=4;
if(i>=num_midis)
num_midis = i + 1;
return i;
}
int sound_alloc_synthdev(void)
{
int i;
for (i = 0; i < MAX_SYNTH_DEV; i++) {
if (synth_devs[i] == NULL) {
if (i >= num_synths)
num_synths++;
return i;
}
}
return -1;
}
int sound_alloc_mixerdev(void)
{
int i = register_sound_mixer(&oss_sound_fops, -1);
if(i==-1)
return -1;
i>>=4;
if(i>=num_mixers)
num_mixers = i + 1;
return i;
}
int sound_alloc_timerdev(void)
{
int i;
for (i = 0; i < MAX_TIMER_DEV; i++) {
if (sound_timer_devs[i] == NULL) {
if (i >= num_sound_timers)
num_sound_timers++;
return i;
}
}
return -1;
}
void sound_unload_mixerdev(int dev)
{
if (dev != -1) {
mixer_devs[dev] = NULL;
unregister_sound_mixer(dev<<4);
num_mixers--;
}
}
void sound_unload_mididev(int dev)
{
if (dev != -1) {
midi_devs[dev] = NULL;
unregister_sound_midi((dev<<4)+2);
}
}
void sound_unload_synthdev(int dev)
{
if (dev != -1)
synth_devs[dev] = NULL;
}
void sound_unload_timerdev(int dev)
{
if (dev != -1)
sound_timer_devs[dev] = NULL;
}

405
sound/oss/dev_table.h Normal file
View File

@@ -0,0 +1,405 @@
/*
* dev_table.h
*
* Global definitions for device call tables
*
*
* 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.
*/
#ifndef _DEV_TABLE_H_
#define _DEV_TABLE_H_
#include <linux/spinlock.h>
/*
* Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
* Numbers 1000 to N are reserved for driver's internal use.
*/
#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */
#define SNDCARD_VIDC 28 /* ARMs VIDC */
#define SNDCARD_SBPNP 29
#define SNDCARD_SOFTOSS 36
#define SNDCARD_VMIDI 37
#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */
#define SNDCARD_OPL3SA1_SB 39
#define SNDCARD_OPL3SA1_MPU 40
#define SNDCARD_WAVEFRONT 41
#define SNDCARD_OPL3SA2 42
#define SNDCARD_OPL3SA2_MPU 43
#define SNDCARD_WAVEARTIST 44 /* Waveartist */
#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */
#define SNDCARD_AD1816 88
/*
* NOTE! NOTE! NOTE! NOTE!
*
* If you modify this file, please check the dev_table.c also.
*
* NOTE! NOTE! NOTE! NOTE!
*/
struct driver_info
{
char *driver_id;
int card_subtype; /* Driver specific. Usually 0 */
int card_type; /* From soundcard.h */
char *name;
void (*attach) (struct address_info *hw_config);
int (*probe) (struct address_info *hw_config);
void (*unload) (struct address_info *hw_config);
};
struct card_info
{
int card_type; /* Link (search key) to the driver list */
struct address_info config;
int enabled;
void *for_driver_use;
};
/*
* Device specific parameters (used only by dmabuf.c)
*/
#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
#define DMODE_NONE 0
#define DMODE_OUTPUT PCM_ENABLE_OUTPUT
#define DMODE_INPUT PCM_ENABLE_INPUT
struct dma_buffparms
{
int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
int closing;
/*
* Pointers to raw buffers
*/
char *raw_buf;
unsigned long raw_buf_phys;
int buffsize;
/*
* Device state tables
*/
unsigned long flags;
#define DMA_BUSY 0x00000001
#define DMA_RESTART 0x00000002
#define DMA_ACTIVE 0x00000004
#define DMA_STARTED 0x00000008
#define DMA_EMPTY 0x00000010
#define DMA_ALLOC_DONE 0x00000020
#define DMA_SYNCING 0x00000040
#define DMA_DIRTY 0x00000080
#define DMA_POST 0x00000100
#define DMA_NODMA 0x00000200
#define DMA_NOTIMEOUT 0x00000400
int open_mode;
/*
* Queue parameters.
*/
int qlen;
int qhead;
int qtail;
spinlock_t lock;
int cfrag; /* Current incomplete fragment (write) */
int nbufs;
int counts[MAX_SUB_BUFFERS];
int subdivision;
int fragment_size;
int needs_reorg;
int max_fragments;
int bytes_in_use;
int underrun_count;
unsigned long byte_counter;
unsigned long user_counter;
unsigned long max_byte_counter;
int data_rate; /* Bytes/second */
int mapping_flags;
#define DMA_MAP_MAPPED 0x00000001
char neutral_byte;
int dma; /* DMA channel */
int applic_profile; /* Application profile (APF_*) */
/* Interrupt callback stuff */
void (*audio_callback) (int dev, int parm);
int callback_parm;
int buf_flags[MAX_SUB_BUFFERS];
#define BUFF_EOF 0x00000001 /* Increment eof count */
#define BUFF_DIRTY 0x00000002 /* Buffer written */
};
/*
* Structure for use with various microcontrollers and DSP processors
* in the recent sound cards.
*/
typedef struct coproc_operations
{
char name[64];
struct module *owner;
int (*open) (void *devc, int sub_device);
void (*close) (void *devc, int sub_device);
int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local);
void (*reset) (void *devc);
void *devc; /* Driver specific info */
} coproc_operations;
struct audio_driver
{
struct module *owner;
int (*open) (int dev, int mode);
void (*close) (int dev);
void (*output_block) (int dev, unsigned long buf,
int count, int intrflag);
void (*start_input) (int dev, unsigned long buf,
int count, int intrflag);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
int (*prepare_for_input) (int dev, int bufsize, int nbufs);
int (*prepare_for_output) (int dev, int bufsize, int nbufs);
void (*halt_io) (int dev);
int (*local_qlen)(int dev);
void (*copy_user) (int dev,
char *localbuf, int localoffs,
const char __user *userbuf, int useroffs,
int max_in, int max_out,
int *used, int *returned,
int len);
void (*halt_input) (int dev);
void (*halt_output) (int dev);
void (*trigger) (int dev, int bits);
int (*set_speed)(int dev, int speed);
unsigned int (*set_bits)(int dev, unsigned int bits);
short (*set_channels)(int dev, short channels);
void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */
void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */
void (*mmap)(int dev);
};
struct audio_operations
{
char name[128];
int flags;
#define NOTHING_SPECIAL 0x00
#define NEEDS_RESTART 0x01
#define DMA_AUTOMODE 0x02
#define DMA_DUPLEX 0x04
#define DMA_PSEUDO_AUTOMODE 0x08
#define DMA_HARDSTOP 0x10
#define DMA_EXACT 0x40
#define DMA_NORESET 0x80
int format_mask; /* Bitmask for supported audio formats */
void *devc; /* Driver specific info */
struct audio_driver *d;
void *portc; /* Driver specific info */
struct dma_buffparms *dmap_in, *dmap_out;
struct coproc_operations *coproc;
int mixer_dev;
int enable_bits;
int open_mode;
int go;
int min_fragment; /* 0 == unlimited */
int max_fragment; /* 0 == unlimited */
int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
/* fields formerly in dmabuf.c */
wait_queue_head_t in_sleeper;
wait_queue_head_t out_sleeper;
wait_queue_head_t poll_sleeper;
/* fields formerly in audio.c */
int audio_mode;
#define AM_NONE 0
#define AM_WRITE OPEN_WRITE
#define AM_READ OPEN_READ
int local_format;
int audio_format;
int local_conversion;
#define CNV_MU_LAW 0x00000001
/* large structures at the end to keep offsets small */
struct dma_buffparms dmaps[2];
};
int *load_mixer_volumes(char *name, int *levels, int present);
struct mixer_operations
{
struct module *owner;
char id[16];
char name[64];
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
void *devc;
int modify_counter;
};
struct synth_operations
{
struct module *owner;
char *id; /* Unique identifier (ASCII) max 29 char */
struct synth_info *info;
int midi_dev;
int synth_type;
int synth_subtype;
int (*open) (int dev, int mode);
void (*close) (int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
int (*kill_note) (int dev, int voice, int note, int velocity);
int (*start_note) (int dev, int voice, int note, int velocity);
int (*set_instr) (int dev, int voice, int instr);
void (*reset) (int dev);
void (*hw_control) (int dev, unsigned char *event);
int (*load_patch) (int dev, int format, const char __user *addr,
int offs, int count, int pmgr_flag);
void (*aftertouch) (int dev, int voice, int pressure);
void (*controller) (int dev, int voice, int ctrl_num, int value);
void (*panning) (int dev, int voice, int value);
void (*volume_method) (int dev, int mode);
void (*bender) (int dev, int chn, int value);
int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
void (*setup_voice) (int dev, int voice, int chn);
int (*send_sysex)(int dev, unsigned char *bytes, int len);
struct voice_alloc_info alloc;
struct channel_info chn_info[16];
int emulation;
#define EMU_GM 1 /* General MIDI */
#define EMU_XG 2 /* Yamaha XG */
#define MAX_SYSEX_BUF 64
unsigned char sysex_buf[MAX_SYSEX_BUF];
int sysex_ptr;
};
struct midi_input_info
{
/* MIDI input scanner variables */
#define MI_MAX 10
volatile int m_busy;
unsigned char m_buf[MI_MAX];
unsigned char m_prev_status; /* For running status */
int m_ptr;
#define MST_INIT 0
#define MST_DATA 1
#define MST_SYSEX 2
int m_state;
int m_left;
};
struct midi_operations
{
struct module *owner;
struct midi_info info;
struct synth_operations *converter;
struct midi_input_info in_info;
int (*open) (int dev, int mode,
void (*inputintr)(int dev, unsigned char data),
void (*outputintr)(int dev)
);
void (*close) (int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
int (*outputc) (int dev, unsigned char data);
int (*start_read) (int dev);
int (*end_read) (int dev);
void (*kick)(int dev);
int (*command) (int dev, unsigned char *data);
int (*buffer_status) (int dev);
int (*prefix_cmd) (int dev, unsigned char status);
struct coproc_operations *coproc;
void *devc;
};
struct sound_lowlev_timer
{
int dev;
int priority;
unsigned int (*tmr_start)(int dev, unsigned int usecs);
void (*tmr_disable)(int dev);
void (*tmr_restart)(int dev);
};
struct sound_timer_operations
{
struct module *owner;
struct sound_timer_info info;
int priority;
int devlink;
int (*open)(int dev, int mode);
void (*close)(int dev);
int (*event)(int dev, unsigned char *ev);
unsigned long (*get_time)(int dev);
int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
void (*arm_timer)(int dev, long time);
};
#ifdef _DEV_TABLE_C_
struct audio_operations *audio_devs[MAX_AUDIO_DEV];
int num_audiodevs;
struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
int num_mixers;
struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
int num_synths;
struct midi_operations *midi_devs[MAX_MIDI_DEV];
int num_midis;
extern struct sound_timer_operations default_sound_timer;
struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
&default_sound_timer, NULL
};
int num_sound_timers = 1;
#else
extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
extern int num_audiodevs;
extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
extern int num_mixers;
extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
extern int num_synths;
extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
extern int num_midis;
extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
extern int num_sound_timers;
#endif /* _DEV_TABLE_C_ */
extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
void sound_timer_init (struct sound_lowlev_timer *t, char *name);
void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
#define AUDIO_DRIVER_VERSION 2
#define MIXER_DRIVER_VERSION 2
int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
int driver_size, int flags, unsigned int format_mask,
void *devc, int dma1, int dma2);
int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
int driver_size, void *devc);
void sound_unload_audiodev(int dev);
void sound_unload_mixerdev(int dev);
void sound_unload_mididev(int dev);
void sound_unload_synthdev(int dev);
void sound_unload_timerdev(int dev);
int sound_alloc_mixerdev(void);
int sound_alloc_timerdev(void);
int sound_alloc_synthdev(void);
int sound_alloc_mididev(void);
#endif /* _DEV_TABLE_H_ */

79
sound/oss/dm.h Normal file
View File

@@ -0,0 +1,79 @@
#ifndef _DRIVERS_SOUND_DM_H
#define _DRIVERS_SOUND_DM_H
/*
* Definitions of the 'direct midi sound' interface used
* by the newer commercial OSS package. We should export
* this to userland somewhere in glibc later.
*/
/*
* Data structure composing an FM "note" or sound event.
*/
struct dm_fm_voice
{
u8 op;
u8 voice;
u8 am;
u8 vibrato;
u8 do_sustain;
u8 kbd_scale;
u8 harmonic;
u8 scale_level;
u8 volume;
u8 attack;
u8 decay;
u8 sustain;
u8 release;
u8 feedback;
u8 connection;
u8 left;
u8 right;
u8 waveform;
};
/*
* This describes an FM note by its voice, octave, frequency number (10bit)
* and key on/off.
*/
struct dm_fm_note
{
u8 voice;
u8 octave;
u32 fnum;
u8 key_on;
};
/*
* FM parameters that apply globally to all voices, and thus are not "notes"
*/
struct dm_fm_params
{
u8 am_depth;
u8 vib_depth;
u8 kbd_split;
u8 rhythm;
/* This block is the percussion instrument data */
u8 bass;
u8 snare;
u8 tomtom;
u8 cymbal;
u8 hihat;
};
/*
* FM mode ioctl settings
*/
#define FM_IOCTL_RESET 0x20
#define FM_IOCTL_PLAY_NOTE 0x21
#define FM_IOCTL_SET_VOICE 0x22
#define FM_IOCTL_SET_PARAMS 0x23
#define FM_IOCTL_SET_MODE 0x24
#define FM_IOCTL_SET_OPL 0x25
#endif

1298
sound/oss/dmabuf.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
config DMASOUND_ATARI
tristate "Atari DMA sound support"
depends on ATARI && SOUND
select DMASOUND
help
If you want to use the internal audio of your Atari in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND_PMAC
tristate "PowerMac DMA sound support"
depends on PPC32 && PPC_PMAC && SOUND && I2C
select DMASOUND
help
If you want to use the internal audio of your PowerMac in Linux,
answer Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND_PAULA
tristate "Amiga DMA sound support"
depends on (AMIGA || APUS) && SOUND
select DMASOUND
help
If you want to use the internal audio of your Amiga in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND_Q40
tristate "Q40 sound support"
depends on Q40 && SOUND
select DMASOUND
help
If you want to use the internal audio of your Q40 in Linux, answer
Y to this question. This will provide a Sun-like /dev/audio,
compatible with the Linux/i386 sound system. Otherwise, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>.
config DMASOUND
tristate

View File

@@ -0,0 +1,13 @@
#
# Makefile for the DMA sound driver
#
dmasound_pmac-y += dmasound_awacs.o \
trans_16.o dac3550a.o tas_common.o \
tas3001c.o tas3001c_tables.o \
tas3004.o tas3004_tables.o
obj-$(CONFIG_DMASOUND_ATARI) += dmasound_core.o dmasound_atari.o
obj-$(CONFIG_DMASOUND_PMAC) += dmasound_core.o dmasound_pmac.o
obj-$(CONFIG_DMASOUND_PAULA) += dmasound_core.o dmasound_paula.o
obj-$(CONFIG_DMASOUND_Q40) += dmasound_core.o dmasound_q40.o

View File

@@ -0,0 +1,251 @@
/*********************************************************/
/* This file was written by someone, somewhere, sometime */
/* And is released into the Public Domain */
/*********************************************************/
#ifndef _AWACS_DEFS_H_
#define _AWACS_DEFS_H_
/*******************************/
/* AWACs Audio Register Layout */
/*******************************/
struct awacs_regs {
unsigned control; /* Audio control register */
unsigned pad0[3];
unsigned codec_ctrl; /* Codec control register */
unsigned pad1[3];
unsigned codec_stat; /* Codec status register */
unsigned pad2[3];
unsigned clip_count; /* Clipping count register */
unsigned pad3[3];
unsigned byteswap; /* Data is little-endian if 1 */
};
/*******************/
/* Audio Bit Masks */
/*******************/
/* Audio Control Reg Bit Masks */
/* ----- ------- --- --- ----- */
#define MASK_ISFSEL (0xf) /* Input SubFrame Select */
#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */
#define MASK_RATE (0x7 << 8) /* Sound Rate */
#define MASK_CNTLERR (0x1 << 11) /* Error */
#define MASK_PORTCHG (0x1 << 12) /* Port Change */
#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */
#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */
#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */
/* Audio Codec Control Reg Bit Masks */
/* ----- ----- ------- --- --- ----- */
#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */
#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */
#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */
#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */
/* Audio Codec Control Address Values / Masks */
/* ----- ----- ------- ------- ------ - ----- */
#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */
#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */
#define MASK_ADDR_GAIN MASK_ADDR0
#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */
#define MASK_ADDR_MUTE MASK_ADDR1
#define MASK_ADDR_RATE MASK_ADDR1
#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */
#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */
#define MASK_ADDR_VOLHD MASK_ADDR2
#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */
#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */
#define MASK_ADDR_VOLSPK MASK_ADDR4
/* additional registers of screamer */
#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */
#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */
#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */
/* Address 0 Bit Masks & Macros */
/* ------- - --- ----- - ------ */
#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */
#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */
#define MASK_GAINLINE (0x1 << 8) /* Disable Mic preamp */
#define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */
#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */
#define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */
#define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */
#define MASK_MUX_LINE MASK_MUX_AUDIN
#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT)
#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT)
#define DEF_CD_GAIN 0x00bb
#define DEF_MIC_GAIN 0x00cc
/* Address 1 Bit Masks */
/* ------- - --- ----- */
#define MASK_ADDR1RES1 (0x3) /* Reserved */
#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */
#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */
#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */
#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */
#define MASK_SPKMUTE MASK_CMUTE
#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */
#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */
#define MASK_HDMUTE MASK_AMUTE
#define MASK_PAROUT0 (0x1 << 10) /* Parallel Output 0 */
#define MASK_PAROUT1 (0x2 << 10) /* Parallel Output 1 */
#define MASK_MIC_BOOST (0x4) /* screamer mic boost */
#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */
#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */
#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */
#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */
#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */
#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */
#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */
#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */
/* Address 2 & 4 Bit Masks & Macros */
/* ------- - - - --- ----- - ------ */
#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */
#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */
#define MASK_ADDR4RES1 MASK_ADDR2RES1
#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */
#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */
#define MASK_ADDR4RES2 MASK_ADDR2RES2
#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT))
#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT)
/* Audio Codec Status Reg Bit Masks */
/* ----- ----- ------ --- --- ----- */
#define MASK_EXTEND (0x1 << 23) /* Extend */
#define MASK_VALID (0x1 << 22) /* Valid Data? */
#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */
#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */
#define MASK_ERRCODE (0xf << 16) /* Error Code */
#define MASK_REVISION (0xf << 12) /* Revision Number */
#define MASK_MFGID (0xf << 8) /* Mfg. ID */
#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */
#define MASK_INPPORT (0xf) /* Input Port */
#define MASK_HDPCONN 8 /* headphone plugged in */
/* Clipping Count Reg Bit Masks */
/* -------- ----- --- --- ----- */
#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */
#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */
/* DBDMA ChannelStatus Bit Masks */
/* ----- ------------- --- ----- */
#define MASK_CSERR (0x1 << 7) /* Error */
#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */
#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */
#define MASK_WAIT (0x1) /* Wait */
/* Various Rates */
/* ------- ----- */
#define RATE_48000 (0x0 << 8) /* 48 kHz */
#define RATE_44100 (0x0 << 8) /* 44.1 kHz */
#define RATE_32000 (0x1 << 8) /* 32 kHz */
#define RATE_29400 (0x1 << 8) /* 29.4 kHz */
#define RATE_24000 (0x2 << 8) /* 24 kHz */
#define RATE_22050 (0x2 << 8) /* 22.05 kHz */
#define RATE_19200 (0x3 << 8) /* 19.2 kHz */
#define RATE_17640 (0x3 << 8) /* 17.64 kHz */
#define RATE_16000 (0x4 << 8) /* 16 kHz */
#define RATE_14700 (0x4 << 8) /* 14.7 kHz */
#define RATE_12000 (0x5 << 8) /* 12 kHz */
#define RATE_11025 (0x5 << 8) /* 11.025 kHz */
#define RATE_9600 (0x6 << 8) /* 9.6 kHz */
#define RATE_8820 (0x6 << 8) /* 8.82 kHz */
#define RATE_8000 (0x7 << 8) /* 8 kHz */
#define RATE_7350 (0x7 << 8) /* 7.35 kHz */
#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */
/*******************/
/* Burgundy values */
/*******************/
#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
/* These are all default values for the burgundy */
#define DEF_BURGUNDY_INPSEL21 (0xAA)
#define DEF_BURGUNDY_INPSEL3 (0x0A)
#define DEF_BURGUNDY_GAINCD (0x33)
#define DEF_BURGUNDY_GAINLINE (0x44)
#define DEF_BURGUNDY_GAINMIC (0x44)
#define DEF_BURGUNDY_GAINMODEM (0x06)
/* Remember: lowest volume here is 0x9b */
#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
#define DEF_BURGUNDY_VOLLINE (0x00000000)
#define DEF_BURGUNDY_VOLMIC (0x00000000)
#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)
#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)
/*********************/
/* i2s layout values */
/*********************/
#define I2S_REG_INT_CTL 0x00
#define I2S_REG_SERIAL_FORMAT 0x10
#define I2S_REG_CODEC_MSG_OUT 0x20
#define I2S_REG_CODEC_MSG_IN 0x30
#define I2S_REG_FRAME_COUNT 0x40
#define I2S_REG_FRAME_MATCH 0x50
#define I2S_REG_DATAWORD_SIZES 0x60
#define I2S_REG_PEAKLEVEL_SEL 0x70
#define I2S_REG_PEAKLEVEL_IN0 0x80
#define I2S_REG_PEAKLEVEL_IN1 0x90
#endif /* _AWACS_DEFS_H_ */

View File

@@ -0,0 +1,210 @@
/*
* Driver for the i2c/i2s based DAC3550a sound chip used
* on some Apple iBooks. Also known as "DACA".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include "dmasound.h"
/* FYI: This code was derived from the tas3001c.c Texas/Tumbler mixer
* control code, as well as info derived from the AppleDACAAudio driver
* from Darwin CVS (main thing I derived being register numbers and
* values, as well as when to make the calls). */
#define I2C_DRIVERID_DACA (0xFDCB)
#define DACA_VERSION "0.1"
#define DACA_DATE "20010930"
static int cur_left_vol;
static int cur_right_vol;
static struct i2c_client *daca_client;
static int daca_attach_adapter(struct i2c_adapter *adapter);
static int daca_detect_client(struct i2c_adapter *adapter, int address);
static int daca_detach_client(struct i2c_client *client);
struct i2c_driver daca_driver = {
.owner = THIS_MODULE,
.name = "DAC3550A driver V " DACA_VERSION,
.id = I2C_DRIVERID_DACA,
.flags = I2C_DF_NOTIFY,
.attach_adapter = daca_attach_adapter,
.detach_client = daca_detach_client,
};
#define VOL_MAX ((1<<20) - 1)
void daca_get_volume(uint * left_vol, uint *right_vol)
{
*left_vol = cur_left_vol >> 5;
*right_vol = cur_right_vol >> 5;
}
int daca_set_volume(uint left_vol, uint right_vol)
{
unsigned short voldata;
if (!daca_client)
return -1;
/* Derived from experience, not from any specific values */
left_vol <<= 5;
right_vol <<= 5;
if (left_vol > VOL_MAX)
left_vol = VOL_MAX;
if (right_vol > VOL_MAX)
right_vol = VOL_MAX;
voldata = ((left_vol >> 14) & 0x3f) << 8;
voldata |= (right_vol >> 14) & 0x3f;
if (i2c_smbus_write_word_data(daca_client, 2, voldata) < 0) {
printk("daca: failed to set volume \n");
return -1;
}
cur_left_vol = left_vol;
cur_right_vol = right_vol;
return 0;
}
int daca_leave_sleep(void)
{
if (!daca_client)
return -1;
/* Do a short sleep, just to make sure I2C bus is awake and paying
* attention to us
*/
msleep(20);
/* Write the sample rate reg the value it needs */
i2c_smbus_write_byte_data(daca_client, 1, 8);
daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
/* Another short delay, just to make sure the other I2C bus writes
* have taken...
*/
msleep(20);
/* Write the global config reg - invert right power amp,
* DAC on, use 5-volt mode */
i2c_smbus_write_byte_data(daca_client, 3, 0x45);
return 0;
}
int daca_enter_sleep(void)
{
if (!daca_client)
return -1;
i2c_smbus_write_byte_data(daca_client, 1, 8);
daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
/* Write the global config reg - invert right power amp,
* DAC on, enter low-power mode, use 5-volt mode
*/
i2c_smbus_write_byte_data(daca_client, 3, 0x65);
return 0;
}
static int daca_attach_adapter(struct i2c_adapter *adapter)
{
if (!strncmp(adapter->name, "mac-io", 6))
daca_detect_client(adapter, 0x4d);
return 0;
}
static int daca_init_client(struct i2c_client * new_client)
{
/*
* Probe is not working with the current i2c-keywest
* driver. We try to use addr 0x4d on each adapters
* instead, by setting the format register.
*
* FIXME: I'm sure that can be obtained from the
* device-tree. --BenH.
*/
/* Write the global config reg - invert right power amp,
* DAC on, use 5-volt mode
*/
if (i2c_smbus_write_byte_data(new_client, 3, 0x45))
return -1;
i2c_smbus_write_byte_data(new_client, 1, 8);
daca_client = new_client;
daca_set_volume(15000, 15000);
return 0;
}
static int daca_detect_client(struct i2c_adapter *adapter, int address)
{
const char *client_name = "DAC 3550A Digital Equalizer";
struct i2c_client *new_client;
int rc = -ENODEV;
new_client = kmalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client)
return -ENOMEM;
memset(new_client, 0, sizeof(*new_client));
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &daca_driver;
new_client->flags = 0;
strcpy(new_client->name, client_name);
if (daca_init_client(new_client))
goto bail;
/* Tell the i2c layer a new client has arrived */
if (i2c_attach_client(new_client))
goto bail;
return 0;
bail:
kfree(new_client);
return rc;
}
static int daca_detach_client(struct i2c_client *client)
{
if (client == daca_client)
daca_client = NULL;
i2c_detach_client(client);
kfree(client);
return 0;
}
void daca_cleanup(void)
{
i2c_del_driver(&daca_driver);
}
int daca_init(void)
{
printk("dac3550a driver version %s (%s)\n",DACA_VERSION,DACA_DATE);
return i2c_add_driver(&daca_driver);
}

View File

@@ -0,0 +1,277 @@
#ifndef _dmasound_h_
/*
* linux/sound/oss/dmasound/dmasound.h
*
*
* Minor numbers for the sound driver.
*
* Unfortunately Creative called the codec chip of SB as a DSP. For this
* reason the /dev/dsp is reserved for digitized audio use. There is a
* device for true DSP processors but it will be called something else.
* In v3.0 it's /dev/sndproc but this could be a temporary solution.
*/
#define _dmasound_h_
#include <linux/types.h>
#include <linux/config.h>
#define SND_NDEVS 256 /* Number of supported devices */
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
synthesizer and MIDI output) */
#define SND_DEV_MIDIN 2 /* Raw midi access */
#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
#define SND_DEV_STATUS 6 /* /dev/sndstat */
/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
#define SND_DEV_PSS SND_DEV_SNDPROC
/* switch on various prinks */
#define DEBUG_DMASOUND 1
#define MAX_AUDIO_DEV 5
#define MAX_MIXER_DEV 4
#define MAX_SYNTH_DEV 3
#define MAX_MIDI_DEV 6
#define MAX_TIMER_DEV 3
#define MAX_CATCH_RADIUS 10
#define le2be16(x) (((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
#define le2be16dbl(x) (((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
#define IOCTL_IN(arg, ret) \
do { int error = get_user(ret, (int __user *)(arg)); \
if (error) return error; \
} while (0)
#define IOCTL_OUT(arg, ret) ioctl_return((int __user *)(arg), ret)
static inline int ioctl_return(int __user *addr, int value)
{
return value < 0 ? value : put_user(value, addr);
}
/*
* Configuration
*/
#undef HAS_8BIT_TABLES
#undef HAS_RECORD
#if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
#define HAS_8BIT_TABLES
#define MIN_BUFFERS 4
#define MIN_BUFSIZE (1<<12) /* in bytes (- where does this come from ?) */
#define MIN_FRAG_SIZE 8 /* not 100% sure about this */
#define MAX_BUFSIZE (1<<17) /* Limit for Amiga is 128 kb */
#define MAX_FRAG_SIZE 15 /* allow *4 for mono-8 => stereo-16 (for multi) */
#else /* is pmac and multi is off */
#define MIN_BUFFERS 2
#define MIN_BUFSIZE (1<<8) /* in bytes */
#define MIN_FRAG_SIZE 8
#define MAX_BUFSIZE (1<<18) /* this is somewhat arbitrary for pmac */
#define MAX_FRAG_SIZE 16 /* need to allow *4 for mono-8 => stereo-16 */
#endif
#define DEFAULT_N_BUFFERS 4
#define DEFAULT_BUFF_SIZE (1<<15)
#if defined(CONFIG_DMASOUND_PMAC) || defined(CONFIG_DMASOUND_PMAC_MODULE)
#define HAS_RECORD
#endif
/*
* Initialization
*/
extern int dmasound_init(void);
#ifdef MODULE
extern void dmasound_deinit(void);
#else
#define dmasound_deinit() do { } while (0)
#endif
/* description of the set-up applies to either hard or soft settings */
typedef struct {
int format; /* AFMT_* */
int stereo; /* 0 = mono, 1 = stereo */
int size; /* 8/16 bit*/
int speed; /* speed */
} SETTINGS;
/*
* Machine definitions
*/
typedef struct {
const char *name;
const char *name2;
struct module *owner;
void *(*dma_alloc)(unsigned int, int);
void (*dma_free)(void *, unsigned int);
int (*irqinit)(void);
#ifdef MODULE
void (*irqcleanup)(void);
#endif
void (*init)(void);
void (*silence)(void);
int (*setFormat)(int);
int (*setVolume)(int);
int (*setBass)(int);
int (*setTreble)(int);
int (*setGain)(int);
void (*play)(void);
void (*record)(void); /* optional */
void (*mixer_init)(void); /* optional */
int (*mixer_ioctl)(u_int, u_long); /* optional */
int (*write_sq_setup)(void); /* optional */
int (*read_sq_setup)(void); /* optional */
int (*sq_open)(mode_t); /* optional */
int (*state_info)(char *, size_t); /* optional */
void (*abort_read)(void); /* optional */
int min_dsp_speed;
int max_dsp_speed;
int version ;
int hardware_afmts ; /* OSS says we only return h'ware info */
/* when queried via SNDCTL_DSP_GETFMTS */
int capabilities ; /* low-level reply to SNDCTL_DSP_GETCAPS */
SETTINGS default_hard ; /* open() or init() should set something valid */
SETTINGS default_soft ; /* you can make it look like old OSS, if you want to */
} MACHINE;
/*
* Low level stuff
*/
typedef struct {
ssize_t (*ct_ulaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_alaw)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u8)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u16be)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_s16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
ssize_t (*ct_u16le)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
} TRANS;
struct sound_settings {
MACHINE mach; /* machine dependent things */
SETTINGS hard; /* hardware settings */
SETTINGS soft; /* software settings */
SETTINGS dsp; /* /dev/dsp default settings */
TRANS *trans_write; /* supported translations */
#ifdef HAS_RECORD
TRANS *trans_read; /* supported translations */
#endif
int volume_left; /* volume (range is machine dependent) */
int volume_right;
int bass; /* tone (range is machine dependent) */
int treble;
int gain;
int minDev; /* minor device number currently open */
spinlock_t lock;
};
extern struct sound_settings dmasound;
#ifdef HAS_8BIT_TABLES
extern char dmasound_ulaw2dma8[];
extern char dmasound_alaw2dma8[];
#endif
/*
* Mid level stuff
*/
static inline int dmasound_set_volume(int volume)
{
return dmasound.mach.setVolume(volume);
}
static inline int dmasound_set_bass(int bass)
{
return dmasound.mach.setBass ? dmasound.mach.setBass(bass) : 50;
}
static inline int dmasound_set_treble(int treble)
{
return dmasound.mach.setTreble ? dmasound.mach.setTreble(treble) : 50;
}
static inline int dmasound_set_gain(int gain)
{
return dmasound.mach.setGain ? dmasound.mach.setGain(gain) : 100;
}
/*
* Sound queue stuff, the heart of the driver
*/
struct sound_queue {
/* buffers allocated for this queue */
int numBufs; /* real limits on what the user can have */
int bufSize; /* in bytes */
char **buffers;
/* current parameters */
int locked ; /* params cannot be modified when != 0 */
int user_frags ; /* user requests this many */
int user_frag_size ; /* of this size */
int max_count; /* actual # fragments <= numBufs */
int block_size; /* internal block size in bytes */
int max_active; /* in-use fragments <= max_count */
/* it shouldn't be necessary to declare any of these volatile */
int front, rear, count;
int rear_size;
/*
* The use of the playing field depends on the hardware
*
* Atari, PMac: The number of frames that are loaded/playing
*
* Amiga: Bit 0 is set: a frame is loaded
* Bit 1 is set: a frame is playing
*/
int active;
wait_queue_head_t action_queue, open_queue, sync_queue;
int open_mode;
int busy, syncing, xruns, died;
};
#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ)
#define WAKE_UP(queue) (wake_up_interruptible(&queue))
extern struct sound_queue dmasound_write_sq;
#define write_sq dmasound_write_sq
#ifdef HAS_RECORD
extern struct sound_queue dmasound_read_sq;
#define read_sq dmasound_read_sq
#endif
extern int dmasound_catchRadius;
#define catchRadius dmasound_catchRadius
/* define the value to be put in the byte-swap reg in mac-io
when we want it to swap for us.
*/
#define BS_VAL 1
#define SW_INPUT_VOLUME_SCALE 4
#define SW_INPUT_VOLUME_DEFAULT (128 / SW_INPUT_VOLUME_SCALE)
extern int expand_bal; /* Balance factor for expanding (not volume!) */
extern int expand_read_bal; /* Balance factor for reading */
extern uint software_input_volume; /* software implemented recording volume! */
#endif /* _dmasound_h_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,743 @@
/*
* linux/sound/oss/dmasound/dmasound_paula.c
*
* Amiga `Paula' DMA Sound Driver
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
* - added versioning
* - put in and populated the hardware_afmts field.
* [0.2] - put in SNDCTL_DSP_GETCAPS value.
* [0.3] - put in constraint on state buffer usage.
* [0.4] - put in default hard/soft settings
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/soundcard.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include <asm/machdep.h>
#include "dmasound.h"
#define DMASOUND_PAULA_REVISION 0
#define DMASOUND_PAULA_EDITION 4
/*
* The minimum period for audio depends on htotal (for OCS/ECS/AGA)
* (Imported from arch/m68k/amiga/amisound.c)
*/
extern volatile u_short amiga_audio_min_period;
/*
* amiga_mksound() should be able to restore the period after beeping
* (Imported from arch/m68k/amiga/amisound.c)
*/
extern u_short amiga_audio_period;
/*
* Audio DMA masks
*/
#define AMI_AUDIO_OFF (DMAF_AUD0 | DMAF_AUD1 | DMAF_AUD2 | DMAF_AUD3)
#define AMI_AUDIO_8 (DMAF_SETCLR | DMAF_MASTER | DMAF_AUD0 | DMAF_AUD1)
#define AMI_AUDIO_14 (AMI_AUDIO_8 | DMAF_AUD2 | DMAF_AUD3)
/*
* Helper pointers for 16(14)-bit sound
*/
static int write_sq_block_size_half, write_sq_block_size_quarter;
/*** Low level stuff *********************************************************/
static void *AmiAlloc(unsigned int size, int flags);
static void AmiFree(void *obj, unsigned int size);
static int AmiIrqInit(void);
#ifdef MODULE
static void AmiIrqCleanUp(void);
#endif
static void AmiSilence(void);
static void AmiInit(void);
static int AmiSetFormat(int format);
static int AmiSetVolume(int volume);
static int AmiSetTreble(int treble);
static void AmiPlayNextFrame(int index);
static void AmiPlay(void);
static irqreturn_t AmiInterrupt(int irq, void *dummy, struct pt_regs *fp);
#ifdef CONFIG_HEARTBEAT
/*
* Heartbeat interferes with sound since the 7 kHz low-pass filter and the
* power LED are controlled by the same line.
*/
#ifdef CONFIG_APUS
#define mach_heartbeat ppc_md.heartbeat
#endif
static void (*saved_heartbeat)(int) = NULL;
static inline void disable_heartbeat(void)
{
if (mach_heartbeat) {
saved_heartbeat = mach_heartbeat;
mach_heartbeat = NULL;
}
AmiSetTreble(dmasound.treble);
}
static inline void enable_heartbeat(void)
{
if (saved_heartbeat)
mach_heartbeat = saved_heartbeat;
}
#else /* !CONFIG_HEARTBEAT */
#define disable_heartbeat() do { } while (0)
#define enable_heartbeat() do { } while (0)
#endif /* !CONFIG_HEARTBEAT */
/*** Mid level stuff *********************************************************/
static void AmiMixerInit(void);
static int AmiMixerIoctl(u_int cmd, u_long arg);
static int AmiWriteSqSetup(void);
static int AmiStateInfo(char *buffer, size_t space);
/*** Translations ************************************************************/
/* ++TeSche: radically changed for new expanding purposes...
*
* These two routines now deal with copying/expanding/translating the samples
* from user space into our buffer at the right frequency. They take care about
* how much data there's actually to read, how much buffer space there is and
* to convert samples into the right frequency/encoding. They will only work on
* complete samples so it may happen they leave some bytes in the input stream
* if the user didn't write a multiple of the current sample size. They both
* return the number of bytes they've used from both streams so you may detect
* such a situation. Luckily all programs should be able to cope with that.
*
* I think I've optimized anything as far as one can do in plain C, all
* variables should fit in registers and the loops are really short. There's
* one loop for every possible situation. Writing a more generalized and thus
* parameterized loop would only produce slower code. Feel free to optimize
* this in assembler if you like. :)
*
* I think these routines belong here because they're not yet really hardware
* independent, especially the fact that the Falcon can play 16bit samples
* only in stereo is hardcoded in both of them!
*
* ++geert: split in even more functions (one per format)
*/
/*
* Native format
*/
static ssize_t ami_ct_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed, ssize_t frameLeft)
{
ssize_t count, used;
if (!dmasound.soft.stereo) {
void *p = &frame[*frameUsed];
count = min_t(unsigned long, userCount, frameLeft) & ~1;
used = count;
if (copy_from_user(p, userPtr, count))
return -EFAULT;
} else {
u_char *left = &frame[*frameUsed>>1];
u_char *right = left+write_sq_block_size_half;
count = min_t(unsigned long, userCount, frameLeft)>>1 & ~1;
used = count*2;
while (count > 0) {
if (get_user(*left++, userPtr++)
|| get_user(*right++, userPtr++))
return -EFAULT;
count--;
}
}
*frameUsed += used;
return used;
}
/*
* Copy and convert 8 bit data
*/
#define GENERATE_AMI_CT8(funcname, convsample) \
static ssize_t funcname(const u_char *userPtr, size_t userCount, \
u_char frame[], ssize_t *frameUsed, \
ssize_t frameLeft) \
{ \
ssize_t count, used; \
\
if (!dmasound.soft.stereo) { \
u_char *p = &frame[*frameUsed]; \
count = min_t(size_t, userCount, frameLeft) & ~1; \
used = count; \
while (count > 0) { \
u_char data; \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*p++ = convsample(data); \
count--; \
} \
} else { \
u_char *left = &frame[*frameUsed>>1]; \
u_char *right = left+write_sq_block_size_half; \
count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
used = count*2; \
while (count > 0) { \
u_char data; \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*left++ = convsample(data); \
if (get_user(data, userPtr++)) \
return -EFAULT; \
*right++ = convsample(data); \
count--; \
} \
} \
*frameUsed += used; \
return used; \
}
#define AMI_CT_ULAW(x) (dmasound_ulaw2dma8[(x)])
#define AMI_CT_ALAW(x) (dmasound_alaw2dma8[(x)])
#define AMI_CT_U8(x) ((x) ^ 0x80)
GENERATE_AMI_CT8(ami_ct_ulaw, AMI_CT_ULAW)
GENERATE_AMI_CT8(ami_ct_alaw, AMI_CT_ALAW)
GENERATE_AMI_CT8(ami_ct_u8, AMI_CT_U8)
/*
* Copy and convert 16 bit data
*/
#define GENERATE_AMI_CT_16(funcname, convsample) \
static ssize_t funcname(const u_char *userPtr, size_t userCount, \
u_char frame[], ssize_t *frameUsed, \
ssize_t frameLeft) \
{ \
ssize_t count, used; \
u_short data; \
\
if (!dmasound.soft.stereo) { \
u_char *high = &frame[*frameUsed>>1]; \
u_char *low = high+write_sq_block_size_half; \
count = min_t(size_t, userCount, frameLeft)>>1 & ~1; \
used = count*2; \
while (count > 0) { \
if (get_user(data, ((u_short *)userPtr)++)) \
return -EFAULT; \
data = convsample(data); \
*high++ = data>>8; \
*low++ = (data>>2) & 0x3f; \
count--; \
} \
} else { \
u_char *lefth = &frame[*frameUsed>>2]; \
u_char *leftl = lefth+write_sq_block_size_quarter; \
u_char *righth = lefth+write_sq_block_size_half; \
u_char *rightl = righth+write_sq_block_size_quarter; \
count = min_t(size_t, userCount, frameLeft)>>2 & ~1; \
used = count*4; \
while (count > 0) { \
if (get_user(data, ((u_short *)userPtr)++)) \
return -EFAULT; \
data = convsample(data); \
*lefth++ = data>>8; \
*leftl++ = (data>>2) & 0x3f; \
if (get_user(data, ((u_short *)userPtr)++)) \
return -EFAULT; \
data = convsample(data); \
*righth++ = data>>8; \
*rightl++ = (data>>2) & 0x3f; \
count--; \
} \
} \
*frameUsed += used; \
return used; \
}
#define AMI_CT_S16BE(x) (x)
#define AMI_CT_U16BE(x) ((x) ^ 0x8000)
#define AMI_CT_S16LE(x) (le2be16((x)))
#define AMI_CT_U16LE(x) (le2be16((x)) ^ 0x8000)
GENERATE_AMI_CT_16(ami_ct_s16be, AMI_CT_S16BE)
GENERATE_AMI_CT_16(ami_ct_u16be, AMI_CT_U16BE)
GENERATE_AMI_CT_16(ami_ct_s16le, AMI_CT_S16LE)
GENERATE_AMI_CT_16(ami_ct_u16le, AMI_CT_U16LE)
static TRANS transAmiga = {
.ct_ulaw = ami_ct_ulaw,
.ct_alaw = ami_ct_alaw,
.ct_s8 = ami_ct_s8,
.ct_u8 = ami_ct_u8,
.ct_s16be = ami_ct_s16be,
.ct_u16be = ami_ct_u16be,
.ct_s16le = ami_ct_s16le,
.ct_u16le = ami_ct_u16le,
};
/*** Low level stuff *********************************************************/
static inline void StopDMA(void)
{
custom.aud[0].audvol = custom.aud[1].audvol = 0;
custom.aud[2].audvol = custom.aud[3].audvol = 0;
custom.dmacon = AMI_AUDIO_OFF;
enable_heartbeat();
}
static void *AmiAlloc(unsigned int size, int flags)
{
return amiga_chip_alloc((long)size, "dmasound [Paula]");
}
static void AmiFree(void *obj, unsigned int size)
{
amiga_chip_free (obj);
}
static int __init AmiIrqInit(void)
{
/* turn off DMA for audio channels */
StopDMA();
/* Register interrupt handler. */
if (request_irq(IRQ_AMIGA_AUD0, AmiInterrupt, 0, "DMA sound",
AmiInterrupt))
return 0;
return 1;
}
#ifdef MODULE
static void AmiIrqCleanUp(void)
{
/* turn off DMA for audio channels */
StopDMA();
/* release the interrupt */
free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
}
#endif /* MODULE */
static void AmiSilence(void)
{
/* turn off DMA for audio channels */
StopDMA();
}
static void AmiInit(void)
{
int period, i;
AmiSilence();
if (dmasound.soft.speed)
period = amiga_colorclock/dmasound.soft.speed-1;
else
period = amiga_audio_min_period;
dmasound.hard = dmasound.soft;
dmasound.trans_write = &transAmiga;
if (period < amiga_audio_min_period) {
/* we would need to squeeze the sound, but we won't do that */
period = amiga_audio_min_period;
} else if (period > 65535) {
period = 65535;
}
dmasound.hard.speed = amiga_colorclock/(period+1);
for (i = 0; i < 4; i++)
custom.aud[i].audper = period;
amiga_audio_period = period;
}
static int AmiSetFormat(int format)
{
int size;
/* Amiga sound DMA supports 8bit and 16bit (pseudo 14 bit) modes */
switch (format) {
case AFMT_QUERY:
return dmasound.soft.format;
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_U8:
case AFMT_S8:
size = 8;
break;
case AFMT_S16_BE:
case AFMT_U16_BE:
case AFMT_S16_LE:
case AFMT_U16_LE:
size = 16;
break;
default: /* :-) */
size = 8;
format = AFMT_S8;
}
dmasound.soft.format = format;
dmasound.soft.size = size;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = dmasound.soft.size;
}
AmiInit();
return format;
}
#define VOLUME_VOXWARE_TO_AMI(v) \
(((v) < 0) ? 0 : ((v) > 100) ? 64 : ((v) * 64)/100)
#define VOLUME_AMI_TO_VOXWARE(v) ((v)*100/64)
static int AmiSetVolume(int volume)
{
dmasound.volume_left = VOLUME_VOXWARE_TO_AMI(volume & 0xff);
custom.aud[0].audvol = dmasound.volume_left;
dmasound.volume_right = VOLUME_VOXWARE_TO_AMI((volume & 0xff00) >> 8);
custom.aud[1].audvol = dmasound.volume_right;
if (dmasound.hard.size == 16) {
if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
custom.aud[2].audvol = 1;
custom.aud[3].audvol = 1;
} else {
custom.aud[2].audvol = 0;
custom.aud[3].audvol = 0;
}
}
return VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
(VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
}
static int AmiSetTreble(int treble)
{
dmasound.treble = treble;
if (treble < 50)
ciaa.pra &= ~0x02;
else
ciaa.pra |= 0x02;
return treble;
}
#define AMI_PLAY_LOADED 1
#define AMI_PLAY_PLAYING 2
#define AMI_PLAY_MASK 3
static void AmiPlayNextFrame(int index)
{
u_char *start, *ch0, *ch1, *ch2, *ch3;
u_long size;
/* used by AmiPlay() if all doubts whether there really is something
* to be played are already wiped out.
*/
start = write_sq.buffers[write_sq.front];
size = (write_sq.count == index ? write_sq.rear_size
: write_sq.block_size)>>1;
if (dmasound.hard.stereo) {
ch0 = start;
ch1 = start+write_sq_block_size_half;
size >>= 1;
} else {
ch0 = start;
ch1 = start;
}
disable_heartbeat();
custom.aud[0].audvol = dmasound.volume_left;
custom.aud[1].audvol = dmasound.volume_right;
if (dmasound.hard.size == 8) {
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size;
custom.dmacon = AMI_AUDIO_8;
} else {
size >>= 1;
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size;
if (dmasound.volume_left == 64 && dmasound.volume_right == 64) {
/* We can play pseudo 14-bit only with the maximum volume */
ch3 = ch0+write_sq_block_size_quarter;
ch2 = ch1+write_sq_block_size_quarter;
custom.aud[2].audvol = 1; /* we are being affected by the beeps */
custom.aud[3].audvol = 1; /* restoring volume here helps a bit */
custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
custom.aud[2].audlen = size;
custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
custom.aud[3].audlen = size;
custom.dmacon = AMI_AUDIO_14;
} else {
custom.aud[2].audvol = 0;
custom.aud[3].audvol = 0;
custom.dmacon = AMI_AUDIO_8;
}
}
write_sq.front = (write_sq.front+1) % write_sq.max_count;
write_sq.active |= AMI_PLAY_LOADED;
}
static void AmiPlay(void)
{
int minframes = 1;
custom.intena = IF_AUD0;
if (write_sq.active & AMI_PLAY_LOADED) {
/* There's already a frame loaded */
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
if (write_sq.active & AMI_PLAY_PLAYING)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
if (write_sq.count < minframes) {
/* Nothing to do */
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
if (write_sq.count <= minframes &&
write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
/* hmmm, the only existing frame is not
* yet filled and we're not syncing?
*/
custom.intena = IF_SETCLR | IF_AUD0;
return;
}
AmiPlayNextFrame(minframes);
custom.intena = IF_SETCLR | IF_AUD0;
}
static irqreturn_t AmiInterrupt(int irq, void *dummy, struct pt_regs *fp)
{
int minframes = 1;
custom.intena = IF_AUD0;
if (!write_sq.active) {
/* Playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
WAKE_UP(write_sq.sync_queue);
return IRQ_HANDLED;
}
if (write_sq.active & AMI_PLAY_PLAYING) {
/* We've just finished a frame */
write_sq.count--;
WAKE_UP(write_sq.action_queue);
}
if (write_sq.active & AMI_PLAY_LOADED)
/* Increase threshold: frame 1 is already being played */
minframes = 2;
/* Shift the flags */
write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
if (!write_sq.active)
/* No frame is playing, disable audio DMA */
StopDMA();
custom.intena = IF_SETCLR | IF_AUD0;
if (write_sq.count >= minframes)
/* Try to play the next frame */
AmiPlay();
if (!write_sq.active)
/* Nothing to play anymore.
Wake up a process waiting for audio output to drain. */
WAKE_UP(write_sq.sync_queue);
return IRQ_HANDLED;
}
/*** Mid level stuff *********************************************************/
/*
* /dev/mixer abstraction
*/
static void __init AmiMixerInit(void)
{
dmasound.volume_left = 64;
dmasound.volume_right = 64;
custom.aud[0].audvol = dmasound.volume_left;
custom.aud[3].audvol = 1; /* For pseudo 14bit */
custom.aud[1].audvol = dmasound.volume_right;
custom.aud[2].audvol = 1; /* For pseudo 14bit */
dmasound.treble = 50;
}
static int AmiMixerIoctl(u_int cmd, u_long arg)
{
int data;
switch (cmd) {
case SOUND_MIXER_READ_DEVMASK:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_TREBLE);
case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, 0);
case SOUND_MIXER_READ_STEREODEVS:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME);
case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg,
VOLUME_AMI_TO_VOXWARE(dmasound.volume_left) |
VOLUME_AMI_TO_VOXWARE(dmasound.volume_right) << 8);
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
return IOCTL_OUT(arg, dmasound_set_volume(data));
case SOUND_MIXER_READ_TREBLE:
return IOCTL_OUT(arg, dmasound.treble);
case SOUND_MIXER_WRITE_TREBLE:
IOCTL_IN(arg, data);
return IOCTL_OUT(arg, dmasound_set_treble(data));
}
return -EINVAL;
}
static int AmiWriteSqSetup(void)
{
write_sq_block_size_half = write_sq.block_size>>1;
write_sq_block_size_quarter = write_sq_block_size_half>>1;
return 0;
}
static int AmiStateInfo(char *buffer, size_t space)
{
int len = 0;
len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
dmasound.volume_left);
len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
dmasound.volume_right);
if (len >= space) {
printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
len = space ;
}
return len;
}
/*** Machine definitions *****************************************************/
static SETTINGS def_hard = {
.format = AFMT_S8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static SETTINGS def_soft = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static MACHINE machAmiga = {
.name = "Amiga",
.name2 = "AMIGA",
.owner = THIS_MODULE,
.dma_alloc = AmiAlloc,
.dma_free = AmiFree,
.irqinit = AmiIrqInit,
#ifdef MODULE
.irqcleanup = AmiIrqCleanUp,
#endif /* MODULE */
.init = AmiInit,
.silence = AmiSilence,
.setFormat = AmiSetFormat,
.setVolume = AmiSetVolume,
.setTreble = AmiSetTreble,
.play = AmiPlay,
.mixer_init = AmiMixerInit,
.mixer_ioctl = AmiMixerIoctl,
.write_sq_setup = AmiWriteSqSetup,
.state_info = AmiStateInfo,
.min_dsp_speed = 8000,
.version = ((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
.hardware_afmts = (AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
.capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
};
/*** Config & Setup **********************************************************/
int __init dmasound_paula_init(void)
{
int err;
if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_AUDIO)) {
if (!request_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40,
"dmasound [Paula]"))
return -EBUSY;
dmasound.mach = machAmiga;
dmasound.mach.default_hard = def_hard ;
dmasound.mach.default_soft = def_soft ;
err = dmasound_init();
if (err)
release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
return err;
} else
return -ENODEV;
}
static void __exit dmasound_paula_cleanup(void)
{
dmasound_deinit();
release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
}
module_init(dmasound_paula_init);
module_exit(dmasound_paula_cleanup);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,634 @@
/*
* linux/sound/oss/dmasound/dmasound_q40.c
*
* Q40 DMA Sound Driver
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
* - added versioning
* - put in and populated the hardware_afmts field.
* [0.2] - put in SNDCTL_DSP_GETCAPS value.
* [0.3] - put in default hard/soft settings.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <asm/q40ints.h>
#include <asm/q40_master.h>
#include "dmasound.h"
#define DMASOUND_Q40_REVISION 0
#define DMASOUND_Q40_EDITION 3
static int expand_bal; /* Balance factor for expanding (not volume!) */
static int expand_data; /* Data for expanding */
/*** Low level stuff *********************************************************/
static void *Q40Alloc(unsigned int size, int flags);
static void Q40Free(void *, unsigned int);
static int Q40IrqInit(void);
#ifdef MODULE
static void Q40IrqCleanUp(void);
#endif
static void Q40Silence(void);
static void Q40Init(void);
static int Q40SetFormat(int format);
static int Q40SetVolume(int volume);
static void Q40PlayNextFrame(int index);
static void Q40Play(void);
static irqreturn_t Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp);
static irqreturn_t Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp);
static void Q40Interrupt(void);
/*** Mid level stuff *********************************************************/
/* userCount, frameUsed, frameLeft == byte counts */
static ssize_t q40_ct_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
while (count > 0) {
*p = table[*p]+128;
p++;
count--;
}
*frameUsed += used ;
return used;
}
static ssize_t q40_ct_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
while (count > 0) {
*p = *p + 128;
p++;
count--;
}
*frameUsed += used;
return used;
}
static ssize_t q40_ct_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
u_char *p = (u_char *) &frame[*frameUsed];
used = count = min_t(size_t, userCount, frameLeft);
if (copy_from_user(p,userPtr,count))
return -EFAULT;
*frameUsed += used;
return used;
}
/* a bit too complicated to optimise right now ..*/
static ssize_t q40_ctx_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned char *table = (unsigned char *)
(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
unsigned int data = expand_data;
u_char *p = (u_char *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = table[c];
data += 0x80;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctx_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c ;
data += 0x80;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctx_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c ;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) ;
utotal -= userCount;
return utotal;
}
/* compressing versions */
static ssize_t q40_ctc_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned char *table = (unsigned char *)
(dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
unsigned int data = expand_data;
u_char *p = (u_char *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while(bal<0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = 0x80 + table[c];
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctc_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while (bal < 0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = c + 0x80;
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft);
utotal -= userCount;
return utotal;
}
static ssize_t q40_ctc_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
u_char *p = (u_char *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
while (bal < 0) {
if (userCount == 0)
goto lout;
if (!(bal<(-hSpeed))) {
if (get_user(c, userPtr))
return -EFAULT;
data = c ;
}
userPtr++;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
lout:
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) ;
utotal -= userCount;
return utotal;
}
static TRANS transQ40Normal = {
q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
};
static TRANS transQ40Expanding = {
q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
};
static TRANS transQ40Compressing = {
q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
};
/*** Low level stuff *********************************************************/
static void *Q40Alloc(unsigned int size, int flags)
{
return kmalloc(size, flags); /* change to vmalloc */
}
static void Q40Free(void *ptr, unsigned int size)
{
kfree(ptr);
}
static int __init Q40IrqInit(void)
{
/* Register interrupt handler. */
request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
"DMA sound", Q40Interrupt);
return(1);
}
#ifdef MODULE
static void Q40IrqCleanUp(void)
{
master_outb(0,SAMPLE_ENABLE_REG);
free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
}
#endif /* MODULE */
static void Q40Silence(void)
{
master_outb(0,SAMPLE_ENABLE_REG);
*DAC_LEFT=*DAC_RIGHT=127;
}
static char *q40_pp;
static unsigned int q40_sc;
static void Q40PlayNextFrame(int index)
{
u_char *start;
u_long size;
u_char speed;
/* used by Q40Play() if all doubts whether there really is something
* to be played are already wiped out.
*/
start = write_sq.buffers[write_sq.front];
size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
q40_pp=start;
q40_sc=size;
write_sq.front = (write_sq.front+1) % write_sq.max_count;
write_sq.active++;
speed=(dmasound.hard.speed==10000 ? 0 : 1);
master_outb( 0,SAMPLE_ENABLE_REG);
free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
if (dmasound.soft.stereo)
request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
"Q40 sound", Q40Interrupt);
else
request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
"Q40 sound", Q40Interrupt);
master_outb( speed, SAMPLE_RATE_REG);
master_outb( 1,SAMPLE_CLEAR_REG);
master_outb( 1,SAMPLE_ENABLE_REG);
}
static void Q40Play(void)
{
unsigned long flags;
if (write_sq.active || write_sq.count<=0 ) {
/* There's already a frame loaded */
return;
}
/* nothing in the queue */
if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
/* hmmm, the only existing frame is not
* yet filled and we're not syncing?
*/
return;
}
spin_lock_irqsave(&dmasound.lock, flags);
Q40PlayNextFrame(1);
spin_unlock_irqrestore(&dmasound.lock, flags);
}
static irqreturn_t Q40StereoInterrupt(int irq, void *dummy, struct pt_regs *fp)
{
spin_lock(&dmasound.lock);
if (q40_sc>1){
*DAC_LEFT=*q40_pp++;
*DAC_RIGHT=*q40_pp++;
q40_sc -=2;
master_outb(1,SAMPLE_CLEAR_REG);
}else Q40Interrupt();
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static irqreturn_t Q40MonoInterrupt(int irq, void *dummy, struct pt_regs *fp)
{
spin_lock(&dmasound.lock);
if (q40_sc>0){
*DAC_LEFT=*q40_pp;
*DAC_RIGHT=*q40_pp++;
q40_sc --;
master_outb(1,SAMPLE_CLEAR_REG);
}else Q40Interrupt();
spin_unlock(&dmasound.lock);
return IRQ_HANDLED;
}
static void Q40Interrupt(void)
{
if (!write_sq.active) {
/* playing was interrupted and sq_reset() has already cleared
* the sq variables, so better don't do anything here.
*/
WAKE_UP(write_sq.sync_queue);
master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
goto exit;
} else write_sq.active=0;
write_sq.count--;
Q40Play();
if (q40_sc<2)
{ /* there was nothing to play, disable irq */
master_outb(0,SAMPLE_ENABLE_REG);
*DAC_LEFT=*DAC_RIGHT=127;
}
WAKE_UP(write_sq.action_queue);
exit:
master_outb(1,SAMPLE_CLEAR_REG);
}
static void Q40Init(void)
{
int i, idx;
const int freq[] = {10000, 20000};
/* search a frequency that fits into the allowed error range */
idx = -1;
for (i = 0; i < 2; i++)
if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
idx = i;
dmasound.hard = dmasound.soft;
/*sound.hard.stereo=1;*/ /* no longer true */
dmasound.hard.size=8;
if (idx > -1) {
dmasound.soft.speed = freq[idx];
dmasound.trans_write = &transQ40Normal;
} else
dmasound.trans_write = &transQ40Expanding;
Q40Silence();
if (dmasound.hard.speed > 20200) {
/* squeeze the sound, we do that */
dmasound.hard.speed = 20000;
dmasound.trans_write = &transQ40Compressing;
} else if (dmasound.hard.speed > 10000) {
dmasound.hard.speed = 20000;
} else {
dmasound.hard.speed = 10000;
}
expand_bal = -dmasound.soft.speed;
}
static int Q40SetFormat(int format)
{
/* Q40 sound supports only 8bit modes */
switch (format) {
case AFMT_QUERY:
return(dmasound.soft.format);
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_S8:
case AFMT_U8:
break;
default:
format = AFMT_S8;
}
dmasound.soft.format = format;
dmasound.soft.size = 8;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = 8;
}
Q40Init();
return(format);
}
static int Q40SetVolume(int volume)
{
return 0;
}
/*** Machine definitions *****************************************************/
static SETTINGS def_hard = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 10000
} ;
static SETTINGS def_soft = {
.format = AFMT_U8,
.stereo = 0,
.size = 8,
.speed = 8000
} ;
static MACHINE machQ40 = {
.name = "Q40",
.name2 = "Q40",
.owner = THIS_MODULE,
.dma_alloc = Q40Alloc,
.dma_free = Q40Free,
.irqinit = Q40IrqInit,
#ifdef MODULE
.irqcleanup = Q40IrqCleanUp,
#endif /* MODULE */
.init = Q40Init,
.silence = Q40Silence,
.setFormat = Q40SetFormat,
.setVolume = Q40SetVolume,
.play = Q40Play,
.min_dsp_speed = 10000,
.version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
.hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
.capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
};
/*** Config & Setup **********************************************************/
int __init dmasound_q40_init(void)
{
if (MACH_IS_Q40) {
dmasound.mach = machQ40;
dmasound.mach.default_hard = def_hard ;
dmasound.mach.default_soft = def_soft ;
return dmasound_init();
} else
return -ENODEV;
}
static void __exit dmasound_q40_cleanup(void)
{
dmasound_deinit();
}
module_init(dmasound_q40_init);
module_exit(dmasound_q40_cleanup);
MODULE_DESCRIPTION("Q40/Q60 sound driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,850 @@
/*
* Driver for the i2c/i2s based TA3004 sound chip used
* on some Apple hardware. Also known as "snapper".
*
* Tobias Sargeant <tobias.sargeant@bigpond.com>
* Based upon, tas3001c.c by Christopher C. Chimelis <chris@debian.org>:
*
* TODO:
* -----
* * Enable control over input line 2 (is this connected?)
* * Implement sleep support (at least mute everything and
* * set gains to minimum during sleep)
* * Look into some of Darwin's tweaks regarding the mute
* * lines (delays & different behaviour on some HW)
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "dmasound.h"
#include "tas_common.h"
#include "tas3001c.h"
#include "tas_ioctl.h"
#define TAS3001C_BIQUAD_FILTER_COUNT 6
#define TAS3001C_BIQUAD_CHANNEL_COUNT 2
#define VOL_DEFAULT (100 * 4 / 5)
#define INPUT_DEFAULT (100 * 4 / 5)
#define BASS_DEFAULT (100 / 2)
#define TREBLE_DEFAULT (100 / 2)
struct tas3001c_data_t {
struct tas_data_t super;
int device_id;
int output_id;
int speaker_id;
struct tas_drce_t drce_state;
};
static const union tas_biquad_t
tas3001c_eq_unity={
.buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }
};
static inline unsigned char db_to_regval(short db) {
int r=0;
r=(db+0x59a0) / 0x60;
if (r < 0x91) return 0x91;
if (r > 0xef) return 0xef;
return r;
}
static inline short quantize_db(short db) {
return db_to_regval(db) * 0x60 - 0x59a0;
}
static inline int
register_width(enum tas3001c_reg_t r)
{
switch(r) {
case TAS3001C_REG_MCR:
case TAS3001C_REG_TREBLE:
case TAS3001C_REG_BASS:
return 1;
case TAS3001C_REG_DRC:
return 2;
case TAS3001C_REG_MIXER1:
case TAS3001C_REG_MIXER2:
return 3;
case TAS3001C_REG_VOLUME:
return 6;
case TAS3001C_REG_LEFT_BIQUAD0:
case TAS3001C_REG_LEFT_BIQUAD1:
case TAS3001C_REG_LEFT_BIQUAD2:
case TAS3001C_REG_LEFT_BIQUAD3:
case TAS3001C_REG_LEFT_BIQUAD4:
case TAS3001C_REG_LEFT_BIQUAD5:
case TAS3001C_REG_LEFT_BIQUAD6:
case TAS3001C_REG_RIGHT_BIQUAD0:
case TAS3001C_REG_RIGHT_BIQUAD1:
case TAS3001C_REG_RIGHT_BIQUAD2:
case TAS3001C_REG_RIGHT_BIQUAD3:
case TAS3001C_REG_RIGHT_BIQUAD4:
case TAS3001C_REG_RIGHT_BIQUAD5:
case TAS3001C_REG_RIGHT_BIQUAD6:
return 15;
default:
return 0;
}
}
static int
tas3001c_write_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num,
char *data,
uint write_mode)
{
if (reg_num==TAS3001C_REG_MCR ||
reg_num==TAS3001C_REG_BASS ||
reg_num==TAS3001C_REG_TREBLE) {
return tas_write_byte_register(&self->super,
(uint)reg_num,
*data,
write_mode);
} else {
return tas_write_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data,
write_mode);
}
}
static int
tas3001c_sync_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num)
{
if (reg_num==TAS3001C_REG_MCR ||
reg_num==TAS3001C_REG_BASS ||
reg_num==TAS3001C_REG_TREBLE) {
return tas_sync_byte_register(&self->super,
(uint)reg_num,
register_width(reg_num));
} else {
return tas_sync_register(&self->super,
(uint)reg_num,
register_width(reg_num));
}
}
static int
tas3001c_read_register( struct tas3001c_data_t *self,
enum tas3001c_reg_t reg_num,
char *data,
uint write_mode)
{
return tas_read_register(&self->super,
(uint)reg_num,
register_width(reg_num),
data);
}
static inline int
tas3001c_fast_load(struct tas3001c_data_t *self, int fast)
{
if (fast)
self->super.shadow[TAS3001C_REG_MCR][0] |= 0x80;
else
self->super.shadow[TAS3001C_REG_MCR][0] &= 0x7f;
return tas3001c_sync_register(self,TAS3001C_REG_MCR);
}
static uint
tas3001c_supported_mixers(struct tas3001c_data_t *self)
{
return SOUND_MASK_VOLUME |
SOUND_MASK_PCM |
SOUND_MASK_ALTPCM |
SOUND_MASK_TREBLE |
SOUND_MASK_BASS;
}
static int
tas3001c_mixer_is_stereo(struct tas3001c_data_t *self,int mixer)
{
switch(mixer) {
case SOUND_MIXER_VOLUME:
return 1;
default:
return 0;
}
}
static uint
tas3001c_stereo_mixers(struct tas3001c_data_t *self)
{
uint r=tas3001c_supported_mixers(self);
uint i;
for (i=1; i<SOUND_MIXER_NRDEVICES; i++)
if (r&(1<<i) && !tas3001c_mixer_is_stereo(self,i))
r &= ~(1<<i);
return r;
}
static int
tas3001c_get_mixer_level(struct tas3001c_data_t *self,int mixer,uint *level)
{
if (!self)
return -1;
*level=self->super.mixer[mixer];
return 0;
}
static int
tas3001c_set_mixer_level(struct tas3001c_data_t *self,int mixer,uint level)
{
int rc;
tas_shadow_t *shadow;
uint temp;
uint offset=0;
if (!self)
return -1;
shadow=self->super.shadow;
if (!tas3001c_mixer_is_stereo(self,mixer))
level = tas_mono_to_stereo(level);
switch(mixer) {
case SOUND_MIXER_VOLUME:
temp = tas3001c_gain.master[level&0xff];
shadow[TAS3001C_REG_VOLUME][0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_VOLUME][1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_VOLUME][2] = (temp >> 0) & 0xff;
temp = tas3001c_gain.master[(level>>8)&0xff];
shadow[TAS3001C_REG_VOLUME][3] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_VOLUME][4] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_VOLUME][5] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
break;
case SOUND_MIXER_ALTPCM:
/* tas3001c_fast_load(self, 1); */
level = tas_mono_to_stereo(level);
temp = tas3001c_gain.mixer[level&0xff];
shadow[TAS3001C_REG_MIXER2][offset+0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_MIXER2][offset+1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_MIXER2][offset+2] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
/* tas3001c_fast_load(self, 0); */
break;
case SOUND_MIXER_PCM:
/* tas3001c_fast_load(self, 1); */
level = tas_mono_to_stereo(level);
temp = tas3001c_gain.mixer[level&0xff];
shadow[TAS3001C_REG_MIXER1][offset+0] = (temp >> 16) & 0xff;
shadow[TAS3001C_REG_MIXER1][offset+1] = (temp >> 8) & 0xff;
shadow[TAS3001C_REG_MIXER1][offset+2] = (temp >> 0) & 0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
/* tas3001c_fast_load(self, 0); */
break;
case SOUND_MIXER_TREBLE:
temp = tas3001c_gain.treble[level&0xff];
shadow[TAS3001C_REG_TREBLE][0]=temp&0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
break;
case SOUND_MIXER_BASS:
temp = tas3001c_gain.bass[level&0xff];
shadow[TAS3001C_REG_BASS][0]=temp&0xff;
rc = tas3001c_sync_register(self,TAS3001C_REG_BASS);
break;
default:
rc = -1;
break;
}
if (rc < 0)
return rc;
self->super.mixer[mixer]=level;
return 0;
}
static int
tas3001c_leave_sleep(struct tas3001c_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
if (!self)
return -1;
/* Make sure something answers on the i2c bus */
if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
WRITE_NORMAL|FORCE_WRITE) < 0)
return -1;
tas3001c_fast_load(self, 1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
tas3001c_fast_load(self, 0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
return 0;
}
static int
tas3001c_enter_sleep(struct tas3001c_data_t *self)
{
/* Stub for now, but I have the details on low-power mode */
if (!self)
return -1;
return 0;
}
static int
tas3001c_sync_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter)
{
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
return tas3001c_sync_register(self,reg);
}
static int
tas3001c_write_biquad_shadow( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
SET_4_20(shadow[reg], 0,biquad->coeff.b0);
SET_4_20(shadow[reg], 3,biquad->coeff.b1);
SET_4_20(shadow[reg], 6,biquad->coeff.b2);
SET_4_20(shadow[reg], 9,biquad->coeff.a1);
SET_4_20(shadow[reg],12,biquad->coeff.a2);
return 0;
}
static int
tas3001c_write_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
const union tas_biquad_t *biquad)
{
int rc;
rc=tas3001c_write_biquad_shadow(self, channel, filter, biquad);
if (rc < 0) return rc;
return tas3001c_sync_biquad(self, channel, filter);
}
static int
tas3001c_write_biquad_list( struct tas3001c_data_t *self,
u_int filter_count,
u_int flags,
struct tas_biquad_ctrl_t *biquads)
{
int i;
int rc;
if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
for (i=0; i<filter_count; i++) {
rc=tas3001c_write_biquad(self,
biquads[i].channel,
biquads[i].filter,
&biquads[i].data);
if (rc < 0) break;
}
if (flags & TAS_BIQUAD_FAST_LOAD) {
tas3001c_fast_load(self,0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
}
return rc;
}
static int
tas3001c_read_biquad( struct tas3001c_data_t *self,
u_int channel,
u_int filter,
union tas_biquad_t *biquad)
{
tas_shadow_t *shadow=self->super.shadow;
enum tas3001c_reg_t reg;
if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||
filter >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;
reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;
biquad->coeff.b0=GET_4_20(shadow[reg], 0);
biquad->coeff.b1=GET_4_20(shadow[reg], 3);
biquad->coeff.b2=GET_4_20(shadow[reg], 6);
biquad->coeff.a1=GET_4_20(shadow[reg], 9);
biquad->coeff.a2=GET_4_20(shadow[reg],12);
return 0;
}
static int
tas3001c_eq_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
struct tas_biquad_ctrl_t biquad;
void __user *argp = (void __user *)arg;
if (copy_from_user(&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
rc=tas3001c_write_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static int
tas3001c_eq_list_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
int filter_count;
int flags;
int i,j;
char sync_required[2][6];
struct tas_biquad_ctrl_t biquad;
struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg;
memset(sync_required,0,sizeof(sync_required));
if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int)))
return -EFAULT;
if (copy_from_user(&flags, &argp->flags, sizeof(int)))
return -EFAULT;
if (cmd & SIOC_IN) {
}
for (i=0; i < filter_count; i++) {
if (copy_from_user(&biquad, &argp->biquads[i],
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
if (cmd & SIOC_IN) {
sync_required[biquad.channel][biquad.filter]=1;
rc=tas3001c_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
}
if (cmd & SIOC_OUT) {
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
if (copy_to_user(&argp->biquads[i], &biquad,
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
}
}
if (cmd & SIOC_IN) {
if (flags & TAS_BIQUAD_FAST_LOAD) tas3001c_fast_load(self,1);
for (i=0; i<2; i++) {
for (j=0; j<6; j++) {
if (sync_required[i][j]) {
rc=tas3001c_sync_biquad(self, i, j);
if (rc < 0) return rc;
}
}
}
if (flags & TAS_BIQUAD_FAST_LOAD) {
tas3001c_fast_load(self,0);
/* now we need to set up the mixers again,
because leaving fast mode resets them. */
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
}
}
return 0;
}
static int
tas3001c_update_drce( struct tas3001c_data_t *self,
int flags,
struct tas_drce_t *drce)
{
tas_shadow_t *shadow;
shadow=self->super.shadow;
shadow[TAS3001C_REG_DRC][1] = 0xc1;
if (flags & TAS_DRCE_THRESHOLD) {
self->drce_state.threshold=quantize_db(drce->threshold);
shadow[TAS3001C_REG_DRC][2] = db_to_regval(self->drce_state.threshold);
}
if (flags & TAS_DRCE_ENABLE) {
self->drce_state.enable = drce->enable;
}
if (!self->drce_state.enable) {
shadow[TAS3001C_REG_DRC][0] = 0xf0;
}
#ifdef DEBUG_DRCE
printk("DRCE IOCTL: set [ ENABLE:%x THRESH:%x\n",
self->drce_state.enable,
self->drce_state.threshold);
printk("DRCE IOCTL: reg [ %02x %02x ]\n",
(unsigned char)shadow[TAS3001C_REG_DRC][0],
(unsigned char)shadow[TAS3001C_REG_DRC][1]);
#endif
return tas3001c_sync_register(self, TAS3001C_REG_DRC);
}
static int
tas3001c_drce_rw( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
int rc;
struct tas_drce_ctrl_t drce_ctrl;
void __user *argp = (void __user *)arg;
if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t)))
return -EFAULT;
#ifdef DEBUG_DRCE
printk("DRCE IOCTL: input [ FLAGS:%x ENABLE:%x THRESH:%x\n",
drce_ctrl.flags,
drce_ctrl.data.enable,
drce_ctrl.data.threshold);
#endif
if (cmd & SIOC_IN) {
rc = tas3001c_update_drce(self, drce_ctrl.flags, &drce_ctrl.data);
if (rc < 0)
return rc;
}
if (cmd & SIOC_OUT) {
if (drce_ctrl.flags & TAS_DRCE_ENABLE)
drce_ctrl.data.enable = self->drce_state.enable;
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
drce_ctrl.data.threshold = self->drce_state.threshold;
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
return 0;
}
static void
tas3001c_update_device_parameters(struct tas3001c_data_t *self)
{
int i,j;
if (!self) return;
if (self->output_id == TAS_OUTPUT_HEADPHONES) {
tas3001c_fast_load(self, 1);
for (i=0; i<TAS3001C_BIQUAD_CHANNEL_COUNT; i++) {
for (j=0; j<TAS3001C_BIQUAD_FILTER_COUNT; j++) {
tas3001c_write_biquad(self, i, j, &tas3001c_eq_unity);
}
}
tas3001c_fast_load(self, 0);
(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);
(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);
(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);
(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);
return;
}
for (i=0; tas3001c_eq_prefs[i]; i++) {
struct tas_eq_pref_t *eq = tas3001c_eq_prefs[i];
if (eq->device_id == self->device_id &&
(eq->output_id == 0 || eq->output_id == self->output_id) &&
(eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) {
tas3001c_update_drce(self, TAS_DRCE_ALL, eq->drce);
tas3001c_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads);
break;
}
}
}
static void
tas3001c_device_change_handler(void *self)
{
if (self)
tas3001c_update_device_parameters(self);
}
static struct work_struct device_change;
static int
tas3001c_output_device_change( struct tas3001c_data_t *self,
int device_id,
int output_id,
int speaker_id)
{
self->device_id=device_id;
self->output_id=output_id;
self->speaker_id=speaker_id;
schedule_work(&device_change);
return 0;
}
static int
tas3001c_device_ioctl( struct tas3001c_data_t *self,
u_int cmd,
u_long arg)
{
uint __user *argp = (void __user *)arg;
switch (cmd) {
case TAS_READ_EQ:
case TAS_WRITE_EQ:
return tas3001c_eq_rw(self, cmd, arg);
case TAS_READ_EQ_LIST:
case TAS_WRITE_EQ_LIST:
return tas3001c_eq_list_rw(self, cmd, arg);
case TAS_READ_EQ_FILTER_COUNT:
put_user(TAS3001C_BIQUAD_FILTER_COUNT, argp);
return 0;
case TAS_READ_EQ_CHANNEL_COUNT:
put_user(TAS3001C_BIQUAD_CHANNEL_COUNT, argp);
return 0;
case TAS_READ_DRCE:
case TAS_WRITE_DRCE:
return tas3001c_drce_rw(self, cmd, arg);
case TAS_READ_DRCE_CAPS:
put_user(TAS_DRCE_ENABLE | TAS_DRCE_THRESHOLD, argp);
return 0;
case TAS_READ_DRCE_MIN:
case TAS_READ_DRCE_MAX: {
struct tas_drce_ctrl_t drce_ctrl;
if (copy_from_user(&drce_ctrl, argp,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) {
if (cmd == TAS_READ_DRCE_MIN) {
drce_ctrl.data.threshold=-36<<8;
} else {
drce_ctrl.data.threshold=-6<<8;
}
}
if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
}
return -EINVAL;
}
static int
tas3001c_init_mixer(struct tas3001c_data_t *self)
{
unsigned char mcr = (1<<6)+(2<<4)+(2<<2);
/* Make sure something answers on the i2c bus */
if (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,
WRITE_NORMAL|FORCE_WRITE) < 0)
return -1;
tas3001c_fast_load(self, 1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD6);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);
(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD6);
tas3001c_fast_load(self, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT);
tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT);
return 0;
}
static int
tas3001c_uninit_mixer(struct tas3001c_data_t *self)
{
tas3001c_set_mixer_level(self, SOUND_MIXER_VOLUME, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_PCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_BASS, 0);
tas3001c_set_mixer_level(self, SOUND_MIXER_TREBLE, 0);
return 0;
}
static int
tas3001c_init(struct i2c_client *client)
{
struct tas3001c_data_t *self;
size_t sz = sizeof(*self) + (TAS3001C_REG_MAX*sizeof(tas_shadow_t));
int i, j;
self = kmalloc(sz, GFP_KERNEL);
if (!self)
return -ENOMEM;
memset(self, 0, sz);
self->super.client = client;
self->super.shadow = (tas_shadow_t *)(self+1);
self->output_id = TAS_OUTPUT_HEADPHONES;
dev_set_drvdata(&client->dev, self);
for (i = 0; i < TAS3001C_BIQUAD_CHANNEL_COUNT; i++)
for (j = 0; j < TAS3001C_BIQUAD_FILTER_COUNT; j++)
tas3001c_write_biquad_shadow(self, i, j,
&tas3001c_eq_unity);
INIT_WORK(&device_change, tas3001c_device_change_handler, self);
return 0;
}
static void
tas3001c_uninit(struct tas3001c_data_t *self)
{
tas3001c_uninit_mixer(self);
kfree(self);
}
struct tas_driver_hooks_t tas3001c_hooks = {
.init = (tas_hook_init_t)tas3001c_init,
.post_init = (tas_hook_post_init_t)tas3001c_init_mixer,
.uninit = (tas_hook_uninit_t)tas3001c_uninit,
.get_mixer_level = (tas_hook_get_mixer_level_t)tas3001c_get_mixer_level,
.set_mixer_level = (tas_hook_set_mixer_level_t)tas3001c_set_mixer_level,
.enter_sleep = (tas_hook_enter_sleep_t)tas3001c_enter_sleep,
.leave_sleep = (tas_hook_leave_sleep_t)tas3001c_leave_sleep,
.supported_mixers = (tas_hook_supported_mixers_t)tas3001c_supported_mixers,
.mixer_is_stereo = (tas_hook_mixer_is_stereo_t)tas3001c_mixer_is_stereo,
.stereo_mixers = (tas_hook_stereo_mixers_t)tas3001c_stereo_mixers,
.output_device_change = (tas_hook_output_device_change_t)tas3001c_output_device_change,
.device_ioctl = (tas_hook_device_ioctl_t)tas3001c_device_ioctl
};

View File

@@ -0,0 +1,64 @@
/*
* Header file for the i2c/i2s based TA3001c sound chip used
* on some Apple hardware. Also known as "tumbler".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* Written by Christopher C. Chimelis <chris@debian.org>
*/
#ifndef _TAS3001C_H_
#define _TAS3001C_H_
#include <linux/types.h>
#include "tas_common.h"
#include "tas_eq_prefs.h"
/*
* Macros that correspond to the registers that we write to
* when setting the various values.
*/
#define TAS3001C_VERSION "0.3"
#define TAS3001C_DATE "20011214"
#define I2C_DRIVERNAME_TAS3001C "TAS3001c driver V " TAS3001C_VERSION
#define I2C_DRIVERID_TAS3001C (I2C_DRIVERID_TAS_BASE+0)
extern struct tas_driver_hooks_t tas3001c_hooks;
extern struct tas_gain_t tas3001c_gain;
extern struct tas_eq_pref_t *tas3001c_eq_prefs[];
enum tas3001c_reg_t {
TAS3001C_REG_MCR = 0x01,
TAS3001C_REG_DRC = 0x02,
TAS3001C_REG_VOLUME = 0x04,
TAS3001C_REG_TREBLE = 0x05,
TAS3001C_REG_BASS = 0x06,
TAS3001C_REG_MIXER1 = 0x07,
TAS3001C_REG_MIXER2 = 0x08,
TAS3001C_REG_LEFT_BIQUAD0 = 0x0a,
TAS3001C_REG_LEFT_BIQUAD1 = 0x0b,
TAS3001C_REG_LEFT_BIQUAD2 = 0x0c,
TAS3001C_REG_LEFT_BIQUAD3 = 0x0d,
TAS3001C_REG_LEFT_BIQUAD4 = 0x0e,
TAS3001C_REG_LEFT_BIQUAD5 = 0x0f,
TAS3001C_REG_LEFT_BIQUAD6 = 0x10,
TAS3001C_REG_RIGHT_BIQUAD0 = 0x13,
TAS3001C_REG_RIGHT_BIQUAD1 = 0x14,
TAS3001C_REG_RIGHT_BIQUAD2 = 0x15,
TAS3001C_REG_RIGHT_BIQUAD3 = 0x16,
TAS3001C_REG_RIGHT_BIQUAD4 = 0x17,
TAS3001C_REG_RIGHT_BIQUAD5 = 0x18,
TAS3001C_REG_RIGHT_BIQUAD6 = 0x19,
TAS3001C_REG_MAX = 0x20
};
#endif /* _TAS3001C_H_ */

View File

@@ -0,0 +1,375 @@
#include "tas_common.h"
#include "tas_eq_prefs.h"
static struct tas_drce_t eqp_0e_2_1_drce = {
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0e_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
};
static struct tas_eq_pref_t eqp_0e_2_1 = {
.sample_rate = 44100,
.device_id = 0x0e,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_0e_2_1_drce,
.filter_count = 12,
.biquads = eqp_0e_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_10_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -12.46 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_10_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0F4A12, 0xE16BDA, 0x0F4A12, 0xE173F0, 0x0E9C3A } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x02DD54, 0x05BAA8, 0x02DD54, 0xF8001D, 0x037532 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0E2FC7, 0xE4D5DC, 0x0D7477, 0xE4D5DC, 0x0BA43F } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0E7899, 0xE67CCA, 0x0D0E93, 0xE67CCA, 0x0B872D } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
};
static struct tas_eq_pref_t eqp_10_1_0 = {
.sample_rate = 44100,
.device_id = 0x10,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_10_1_0_drce,
.filter_count = 12,
.biquads = eqp_10_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_15_2_1_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_15_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
};
static struct tas_eq_pref_t eqp_15_2_1 = {
.sample_rate = 44100,
.device_id = 0x15,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_15_2_1_drce,
.filter_count = 12,
.biquads = eqp_15_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_15_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = 0.0 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_15_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FAD08, 0xE0A5EF, 0x0FAD08, 0xE0A79D, 0x0F5BBE } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x04B38D, 0x09671B, 0x04B38D, 0x000F71, 0x02BEC5 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FDD32, 0xE0A56F, 0x0F8A69, 0xE0A56F, 0x0F679C } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0FD284, 0xE135FB, 0x0F2161, 0xE135FB, 0x0EF3E5 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0E81B1, 0xE6283F, 0x0CE49D, 0xE6283F, 0x0B664F } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0F2D62, 0xE98797, 0x0D1E19, 0xE98797, 0x0C4B7B } } },
};
static struct tas_eq_pref_t eqp_15_1_0 = {
.sample_rate = 44100,
.device_id = 0x15,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_15_1_0_drce,
.filter_count = 12,
.biquads = eqp_15_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_0f_2_1_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0f_2_1_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FE143, 0xE05204, 0x0FCCC5, 0xE05266, 0x0FAE6B } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x102383, 0xE03A03, 0x0FA325, 0xE03A03, 0x0FC6A8 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FF2AB, 0xE06285, 0x0FB20A, 0xE06285, 0x0FA4B5 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F544D, 0xE35971, 0x0D8F3A, 0xE35971, 0x0CE388 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x13E1D3, 0xF3ECB5, 0x042227, 0xF3ECB5, 0x0803FA } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0AC119, 0x034181, 0x078AB1, 0x034181, 0x024BCA } } },
};
static struct tas_eq_pref_t eqp_0f_2_1 = {
.sample_rate = 44100,
.device_id = 0x0f,
.output_id = TAS_OUTPUT_EXTERNAL_SPKR,
.speaker_id = 0x01,
.drce = &eqp_0f_2_1_drce,
.filter_count = 12,
.biquads = eqp_0f_2_1_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_0f_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -15.33 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0f_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0FCAD3, 0xE06A58, 0x0FCAD3, 0xE06B09, 0x0F9657 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x041731, 0x082E63, 0x041731, 0xFD8D08, 0x02CFBD } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0FFDC7, 0xE0524C, 0x0FBFAA, 0xE0524C, 0x0FBD72 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0F3D35, 0xE228CA, 0x0EC7B2, 0xE228CA, 0x0E04E8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0FCEBF, 0xE181C2, 0x0F2656, 0xE181C2, 0x0EF516 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0EC417, 0x073E22, 0x0B0633, 0x073E22, 0x09CA4A } } },
};
static struct tas_eq_pref_t eqp_0f_1_0 = {
.sample_rate = 44100,
.device_id = 0x0f,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_0f_1_0_drce,
.filter_count = 12,
.biquads = eqp_0f_1_0_biquads
};
/* ======================================================================== */
static uint tas3001c_master_tab[]={
0x0, 0x75, 0x9c, 0xbb,
0xdb, 0xfb, 0x11e, 0x143,
0x16b, 0x196, 0x1c3, 0x1f5,
0x229, 0x263, 0x29f, 0x2e1,
0x328, 0x373, 0x3c5, 0x41b,
0x478, 0x4dc, 0x547, 0x5b8,
0x633, 0x6b5, 0x740, 0x7d5,
0x873, 0x91c, 0x9d2, 0xa92,
0xb5e, 0xc39, 0xd22, 0xe19,
0xf20, 0x1037, 0x1161, 0x129e,
0x13ed, 0x1551, 0x16ca, 0x185d,
0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
0x21c1, 0x23fa, 0x2655, 0x28d6,
0x2b7c, 0x2e4a, 0x3141, 0x3464,
0x37b4, 0x3b35, 0x3ee9, 0x42d3,
0x46f6, 0x4b53, 0x4ff0, 0x54ce,
0x59f2, 0x5f5f, 0x6519, 0x6b24,
0x7183, 0x783c, 0x7f53, 0x86cc,
0x8ead, 0x96fa, 0x9fba, 0xa8f2,
0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
0xdee8, 0xeb75, 0xf8aa, 0x1068e,
0x1152a, 0x12487, 0x134ad, 0x145a5,
0x1577b, 0x16a37, 0x17df5, 0x192bd,
0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
0x20b55, 0x22727, 0x24456, 0x262f2,
0x2830b
};
static uint tas3001c_mixer_tab[]={
0x0, 0x748, 0x9be, 0xbaf,
0xda4, 0xfb1, 0x11de, 0x1431,
0x16ad, 0x1959, 0x1c37, 0x1f4b,
0x2298, 0x2628, 0x29fb, 0x2e12,
0x327d, 0x3734, 0x3c47, 0x41b4,
0x4787, 0x4dbe, 0x546d, 0x5b86,
0x632e, 0x6b52, 0x7400, 0x7d54,
0x873b, 0x91c6, 0x9d1a, 0xa920,
0xb5e5, 0xc38c, 0xd21b, 0xe18f,
0xf1f5, 0x1036a, 0x1160f, 0x129d6,
0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
0x21c0f, 0x23fa3, 0x26552, 0x28d64,
0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
0x37b44, 0x3b353, 0x3ee94, 0x42d30,
0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
0x71835, 0x783c3, 0x7f52c, 0x86cc0,
0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
0x20b542, 0x227268, 0x244564, 0x262f26,
0x2830af
};
static uint tas3001c_treble_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x71, 0x70, 0x6e, 0x6d,
0x6d, 0x6c, 0x6b, 0x6a,
0x69, 0x68, 0x67, 0x66,
0x65, 0x63, 0x62, 0x62,
0x60, 0x5f, 0x5d, 0x5c,
0x5a, 0x58, 0x56, 0x55,
0x53, 0x51, 0x4f, 0x4c,
0x4a, 0x48, 0x45, 0x43,
0x40, 0x3d, 0x3a, 0x37,
0x35, 0x32, 0x2e, 0x2a,
0x27, 0x22, 0x1e, 0x1a,
0x15, 0x11, 0xc, 0x7,
0x1
};
static uint tas3001c_bass_tab[]={
0x86, 0x83, 0x81, 0x7f,
0x7d, 0x7b, 0x79, 0x78,
0x76, 0x75, 0x74, 0x72,
0x71, 0x6f, 0x6e, 0x6d,
0x6c, 0x6b, 0x69, 0x67,
0x65, 0x64, 0x61, 0x60,
0x5e, 0x5d, 0x5c, 0x5b,
0x5a, 0x59, 0x58, 0x57,
0x56, 0x55, 0x55, 0x54,
0x53, 0x52, 0x50, 0x4f,
0x4d, 0x4c, 0x4b, 0x49,
0x47, 0x45, 0x44, 0x42,
0x41, 0x3f, 0x3e, 0x3d,
0x3c, 0x3b, 0x39, 0x38,
0x37, 0x36, 0x35, 0x34,
0x33, 0x31, 0x30, 0x2f,
0x2e, 0x2c, 0x2b, 0x2b,
0x29, 0x28, 0x27, 0x26,
0x25, 0x24, 0x22, 0x21,
0x20, 0x1e, 0x1c, 0x19,
0x18, 0x18, 0x17, 0x16,
0x15, 0x14, 0x13, 0x12,
0x11, 0x10, 0xf, 0xe,
0xd, 0xb, 0xa, 0x9,
0x8, 0x6, 0x4, 0x2,
0x1
};
struct tas_gain_t tas3001c_gain = {
.master = tas3001c_master_tab,
.treble = tas3001c_treble_tab,
.bass = tas3001c_bass_tab,
.mixer = tas3001c_mixer_tab
};
struct tas_eq_pref_t *tas3001c_eq_prefs[]={
&eqp_0e_2_1,
&eqp_10_1_0,
&eqp_15_2_1,
&eqp_15_1_0,
&eqp_0f_2_1,
&eqp_0f_1_0,
NULL
};

1140
sound/oss/dmasound/tas3004.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
/*
* Header file for the i2c/i2s based TA3004 sound chip used
* on some Apple hardware. Also known as "tumbler".
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* Written by Christopher C. Chimelis <chris@debian.org>
*/
#ifndef _TAS3004_H_
#define _TAS3004_H_
#include <linux/types.h>
#include "tas_common.h"
#include "tas_eq_prefs.h"
/*
* Macros that correspond to the registers that we write to
* when setting the various values.
*/
#define TAS3004_VERSION "0.3"
#define TAS3004_DATE "20011214"
#define I2C_DRIVERNAME_TAS3004 "TAS3004 driver V " TAS3004_VERSION
#define I2C_DRIVERID_TAS3004 (I2C_DRIVERID_TAS_BASE+1)
extern struct tas_driver_hooks_t tas3004_hooks;
extern struct tas_gain_t tas3004_gain;
extern struct tas_eq_pref_t *tas3004_eq_prefs[];
enum tas3004_reg_t {
TAS3004_REG_MCR = 0x01,
TAS3004_REG_DRC = 0x02,
TAS3004_REG_VOLUME = 0x04,
TAS3004_REG_TREBLE = 0x05,
TAS3004_REG_BASS = 0x06,
TAS3004_REG_LEFT_MIXER = 0x07,
TAS3004_REG_RIGHT_MIXER = 0x08,
TAS3004_REG_LEFT_BIQUAD0 = 0x0a,
TAS3004_REG_LEFT_BIQUAD1 = 0x0b,
TAS3004_REG_LEFT_BIQUAD2 = 0x0c,
TAS3004_REG_LEFT_BIQUAD3 = 0x0d,
TAS3004_REG_LEFT_BIQUAD4 = 0x0e,
TAS3004_REG_LEFT_BIQUAD5 = 0x0f,
TAS3004_REG_LEFT_BIQUAD6 = 0x10,
TAS3004_REG_RIGHT_BIQUAD0 = 0x13,
TAS3004_REG_RIGHT_BIQUAD1 = 0x14,
TAS3004_REG_RIGHT_BIQUAD2 = 0x15,
TAS3004_REG_RIGHT_BIQUAD3 = 0x16,
TAS3004_REG_RIGHT_BIQUAD4 = 0x17,
TAS3004_REG_RIGHT_BIQUAD5 = 0x18,
TAS3004_REG_RIGHT_BIQUAD6 = 0x19,
TAS3004_REG_LEFT_LOUD_BIQUAD = 0x21,
TAS3004_REG_RIGHT_LOUD_BIQUAD = 0x22,
TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN = 0x23,
TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN = 0x24,
TAS3004_REG_TEST = 0x29,
TAS3004_REG_ANALOG_CTRL = 0x40,
TAS3004_REG_TEST1 = 0x41,
TAS3004_REG_TEST2 = 0x42,
TAS3004_REG_MCR2 = 0x43,
TAS3004_REG_MAX = 0x44
};
#endif /* _TAS3004_H_ */

View File

@@ -0,0 +1,301 @@
#include "tas3004.h"
#include "tas_eq_prefs.h"
static struct tas_drce_t eqp_17_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -19.12 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_17_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0fd0d4, 0xe05e56, 0x0fd0d4, 0xe05ee1, 0x0fa234 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x0910d7, 0x088e1a, 0x030651, 0x01dcb1, 0x02c892 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0ff895, 0xe0970b, 0x0f7f00, 0xe0970b, 0x0f7795 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0fd1c4, 0xe1ac22, 0x0ec8cf, 0xe1ac22, 0x0e9a94 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0f7c1c, 0xe3cc03, 0x0df786, 0xe3cc03, 0x0d73a2 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x11fb92, 0xf5a1a0, 0x073cd2, 0xf5a1a0, 0x093865 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x0e17a9, 0x068b6c, 0x08a0e5, 0x068b6c, 0x06b88e } } }
};
static struct tas_eq_pref_t eqp_17_1_0 = {
.sample_rate = 44100,
.device_id = 0x17,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_17_1_0_drce,
.filter_count = 14,
.biquads = eqp_17_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_18_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -13.14 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_18_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0f5514, 0xe155d7, 0x0f5514, 0xe15cfa, 0x0eb14b } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x06ec33, 0x02abe3, 0x015eef, 0xf764d9, 0x03922d } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0ef5f2, 0xe67d1f, 0x0bcf37, 0xe67d1f, 0x0ac529 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0db050, 0xe5be4d, 0x0d0c78, 0xe5be4d, 0x0abcc8 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0f1298, 0xe64ec6, 0x0cc03e, 0xe64ec6, 0x0bd2d7 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0c641a, 0x06537a, 0x08d155, 0x06537a, 0x053570 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 } } }
};
static struct tas_eq_pref_t eqp_18_1_0 = {
.sample_rate = 44100,
.device_id = 0x18,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_18_1_0_drce,
.filter_count = 14,
.biquads = eqp_18_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_1a_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -10.75 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_1a_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0fb8fd, 0xe08e04, 0x0fb8fd, 0xe08f40, 0x0f7336 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x06371d, 0x0c6e3a, 0x06371d, 0x05bfd3, 0x031ca2 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0fa1c0, 0xe18692, 0x0f030e, 0xe18692, 0x0ea4ce } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0fe495, 0xe17eff, 0x0f0452, 0xe17eff, 0x0ee8e7 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x100857, 0xe7e71c, 0x0e9599, 0xe7e71c, 0x0e9df1 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0fb26e, 0x06a82c, 0x0db2b4, 0x06a82c, 0x0d6522 } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x11419d, 0xf06cbf, 0x0a4f6e, 0xf06cbf, 0x0b910c } } }
};
static struct tas_eq_pref_t eqp_1a_1_0 = {
.sample_rate = 44100,
.device_id = 0x1a,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_1a_1_0_drce,
.filter_count = 14,
.biquads = eqp_1a_1_0_biquads
};
/* ======================================================================== */
static struct tas_drce_t eqp_1c_1_0_drce={
.enable = 1,
.above = { .val = 3.0 * (1<<8), .expand = 0 },
.below = { .val = 1.0 * (1<<8), .expand = 0 },
.threshold = -14.34 * (1<<8),
.energy = 2.4 * (1<<12),
.attack = 0.013 * (1<<12),
.decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_1c_1_0_biquads[]={
{ .channel = 0, .filter = 0, .data = { .coeff = { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
{ .channel = 0, .filter = 1, .data = { .coeff = { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
{ .channel = 0, .filter = 2, .data = { .coeff = { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
{ .channel = 0, .filter = 3, .data = { .coeff = { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
{ .channel = 0, .filter = 4, .data = { .coeff = { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
{ .channel = 0, .filter = 5, .data = { .coeff = { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
{ .channel = 0, .filter = 6, .data = { .coeff = { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } },
{ .channel = 1, .filter = 0, .data = { .coeff = { 0x0f4f95, 0xe160d4, 0x0f4f95, 0xe1686e, 0x0ea6c5 } } },
{ .channel = 1, .filter = 1, .data = { .coeff = { 0x066b92, 0x0290d4, 0x0148a0, 0xf6853f, 0x03bfc7 } } },
{ .channel = 1, .filter = 2, .data = { .coeff = { 0x0f57dc, 0xe51c91, 0x0dd1cb, 0xe51c91, 0x0d29a8 } } },
{ .channel = 1, .filter = 3, .data = { .coeff = { 0x0df1cb, 0xe4fa84, 0x0d7cdc, 0xe4fa84, 0x0b6ea7 } } },
{ .channel = 1, .filter = 4, .data = { .coeff = { 0x0eba36, 0xe6aa48, 0x0b9f52, 0xe6aa48, 0x0a5989 } } },
{ .channel = 1, .filter = 5, .data = { .coeff = { 0x0caf02, 0x05ef9d, 0x084beb, 0x05ef9d, 0x04faee } } },
{ .channel = 1, .filter = 6, .data = { .coeff = { 0x0fc686, 0xe22947, 0x0e4b5d, 0xe22947, 0x0e11e4 } } }
};
static struct tas_eq_pref_t eqp_1c_1_0 = {
.sample_rate = 44100,
.device_id = 0x1c,
.output_id = TAS_OUTPUT_INTERNAL_SPKR,
.speaker_id = 0x00,
.drce = &eqp_1c_1_0_drce,
.filter_count = 14,
.biquads = eqp_1c_1_0_biquads
};
/* ======================================================================== */
static uint tas3004_master_tab[]={
0x0, 0x75, 0x9c, 0xbb,
0xdb, 0xfb, 0x11e, 0x143,
0x16b, 0x196, 0x1c3, 0x1f5,
0x229, 0x263, 0x29f, 0x2e1,
0x328, 0x373, 0x3c5, 0x41b,
0x478, 0x4dc, 0x547, 0x5b8,
0x633, 0x6b5, 0x740, 0x7d5,
0x873, 0x91c, 0x9d2, 0xa92,
0xb5e, 0xc39, 0xd22, 0xe19,
0xf20, 0x1037, 0x1161, 0x129e,
0x13ed, 0x1551, 0x16ca, 0x185d,
0x1a08, 0x1bcc, 0x1dac, 0x1fa7,
0x21c1, 0x23fa, 0x2655, 0x28d6,
0x2b7c, 0x2e4a, 0x3141, 0x3464,
0x37b4, 0x3b35, 0x3ee9, 0x42d3,
0x46f6, 0x4b53, 0x4ff0, 0x54ce,
0x59f2, 0x5f5f, 0x6519, 0x6b24,
0x7183, 0x783c, 0x7f53, 0x86cc,
0x8ead, 0x96fa, 0x9fba, 0xa8f2,
0xb2a7, 0xbce1, 0xc7a5, 0xd2fa,
0xdee8, 0xeb75, 0xf8aa, 0x1068e,
0x1152a, 0x12487, 0x134ad, 0x145a5,
0x1577b, 0x16a37, 0x17df5, 0x192bd,
0x1a890, 0x1bf7b, 0x1d78d, 0x1f0d1,
0x20b55, 0x22727, 0x24456, 0x262f2,
0x2830b
};
static uint tas3004_mixer_tab[]={
0x0, 0x748, 0x9be, 0xbaf,
0xda4, 0xfb1, 0x11de, 0x1431,
0x16ad, 0x1959, 0x1c37, 0x1f4b,
0x2298, 0x2628, 0x29fb, 0x2e12,
0x327d, 0x3734, 0x3c47, 0x41b4,
0x4787, 0x4dbe, 0x546d, 0x5b86,
0x632e, 0x6b52, 0x7400, 0x7d54,
0x873b, 0x91c6, 0x9d1a, 0xa920,
0xb5e5, 0xc38c, 0xd21b, 0xe18f,
0xf1f5, 0x1036a, 0x1160f, 0x129d6,
0x13ed0, 0x1550c, 0x16ca0, 0x185c9,
0x1a07b, 0x1bcc3, 0x1dab9, 0x1fa75,
0x21c0f, 0x23fa3, 0x26552, 0x28d64,
0x2b7c9, 0x2e4a2, 0x31411, 0x3463b,
0x37b44, 0x3b353, 0x3ee94, 0x42d30,
0x46f55, 0x4b533, 0x4fefc, 0x54ce5,
0x59f25, 0x5f5f6, 0x65193, 0x6b23c,
0x71835, 0x783c3, 0x7f52c, 0x86cc0,
0x8eacc, 0x96fa5, 0x9fba0, 0xa8f1a,
0xb2a71, 0xbce0a, 0xc7a4a, 0xd2fa0,
0xdee7b, 0xeb752, 0xf8a9f, 0x1068e4,
0x1152a3, 0x12486a, 0x134ac8, 0x145a55,
0x1577ac, 0x16a370, 0x17df51, 0x192bc2,
0x1a88f8, 0x1bf7b7, 0x1d78c9, 0x1f0d04,
0x20b542, 0x227268, 0x244564, 0x262f26,
0x2830af
};
static uint tas3004_treble_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x71, 0x68, 0x45, 0x5b,
0x6d, 0x6c, 0x6b, 0x6a,
0x69, 0x68, 0x67, 0x66,
0x65, 0x63, 0x62, 0x62,
0x60, 0x5e, 0x5c, 0x5b,
0x59, 0x57, 0x55, 0x53,
0x52, 0x4f, 0x4d, 0x4a,
0x48, 0x46, 0x43, 0x40,
0x3d, 0x3a, 0x36, 0x33,
0x2f, 0x2c, 0x27, 0x23,
0x1f, 0x1a, 0x15, 0xf,
0x8, 0x5, 0x2, 0x1,
0x1
};
static uint tas3004_bass_tab[]={
0x96, 0x95, 0x95, 0x94,
0x93, 0x92, 0x92, 0x91,
0x90, 0x90, 0x8f, 0x8e,
0x8d, 0x8d, 0x8c, 0x8b,
0x8a, 0x8a, 0x89, 0x88,
0x88, 0x87, 0x86, 0x85,
0x85, 0x84, 0x83, 0x83,
0x82, 0x81, 0x80, 0x80,
0x7f, 0x7e, 0x7e, 0x7d,
0x7c, 0x7b, 0x7b, 0x7a,
0x79, 0x78, 0x78, 0x77,
0x76, 0x76, 0x75, 0x74,
0x73, 0x73, 0x72, 0x71,
0x70, 0x6f, 0x6e, 0x6d,
0x6c, 0x6b, 0x6a, 0x6a,
0x69, 0x67, 0x66, 0x66,
0x65, 0x63, 0x62, 0x62,
0x61, 0x60, 0x5e, 0x5d,
0x5b, 0x59, 0x57, 0x55,
0x53, 0x51, 0x4f, 0x4c,
0x4a, 0x48, 0x46, 0x44,
0x41, 0x3e, 0x3b, 0x38,
0x36, 0x33, 0x2f, 0x2b,
0x28, 0x24, 0x20, 0x1c,
0x17, 0x12, 0xd, 0x7,
0x1
};
struct tas_gain_t tas3004_gain={
.master = tas3004_master_tab,
.treble = tas3004_treble_tab,
.bass = tas3004_bass_tab,
.mixer = tas3004_mixer_tab
};
struct tas_eq_pref_t *tas3004_eq_prefs[]={
&eqp_17_1_0,
&eqp_18_1_0,
&eqp_1a_1_0,
&eqp_1c_1_0,
NULL
};

View File

@@ -0,0 +1,214 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "tas_common.h"
#define CALL0(proc) \
do { \
struct tas_data_t *self; \
if (!tas_client || driver_hooks == NULL) \
return -1; \
self = dev_get_drvdata(&tas_client->dev); \
if (driver_hooks->proc) \
return driver_hooks->proc(self); \
else \
return -EINVAL; \
} while (0)
#define CALL(proc,arg...) \
do { \
struct tas_data_t *self; \
if (!tas_client || driver_hooks == NULL) \
return -1; \
self = dev_get_drvdata(&tas_client->dev); \
if (driver_hooks->proc) \
return driver_hooks->proc(self, ## arg); \
else \
return -EINVAL; \
} while (0)
static u8 tas_i2c_address = 0x34;
static struct i2c_client *tas_client;
static struct device_node* tas_node;
static int tas_attach_adapter(struct i2c_adapter *);
static int tas_detach_client(struct i2c_client *);
struct i2c_driver tas_driver = {
.owner = THIS_MODULE,
.name = "tas",
.flags = I2C_DF_NOTIFY,
.attach_adapter = tas_attach_adapter,
.detach_client = tas_detach_client,
};
struct tas_driver_hooks_t *driver_hooks;
int
tas_register_driver(struct tas_driver_hooks_t *hooks)
{
driver_hooks = hooks;
return 0;
}
int
tas_get_mixer_level(int mixer, uint *level)
{
CALL(get_mixer_level,mixer,level);
}
int
tas_set_mixer_level(int mixer,uint level)
{
CALL(set_mixer_level,mixer,level);
}
int
tas_enter_sleep(void)
{
CALL0(enter_sleep);
}
int
tas_leave_sleep(void)
{
CALL0(leave_sleep);
}
int
tas_supported_mixers(void)
{
CALL0(supported_mixers);
}
int
tas_mixer_is_stereo(int mixer)
{
CALL(mixer_is_stereo,mixer);
}
int
tas_stereo_mixers(void)
{
CALL0(stereo_mixers);
}
int
tas_output_device_change(int device_id,int layout_id,int speaker_id)
{
CALL(output_device_change,device_id,layout_id,speaker_id);
}
int
tas_device_ioctl(u_int cmd, u_long arg)
{
CALL(device_ioctl,cmd,arg);
}
int
tas_post_init(void)
{
CALL0(post_init);
}
static int
tas_detect_client(struct i2c_adapter *adapter, int address)
{
static const char *client_name = "tas Digital Equalizer";
struct i2c_client *new_client;
int rc = -ENODEV;
if (!driver_hooks) {
printk(KERN_ERR "tas_detect_client called with no hooks !\n");
return -ENODEV;
}
new_client = kmalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client)
return -ENOMEM;
memset(new_client, 0, sizeof(*new_client));
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &tas_driver;
strlcpy(new_client->name, client_name, DEVICE_NAME_SIZE);
if (driver_hooks->init(new_client))
goto bail;
/* Tell the i2c layer a new client has arrived */
if (i2c_attach_client(new_client)) {
driver_hooks->uninit(dev_get_drvdata(&new_client->dev));
goto bail;
}
tas_client = new_client;
return 0;
bail:
tas_client = NULL;
kfree(new_client);
return rc;
}
static int
tas_attach_adapter(struct i2c_adapter *adapter)
{
if (!strncmp(adapter->name, "mac-io", 6))
return tas_detect_client(adapter, tas_i2c_address);
return 0;
}
static int
tas_detach_client(struct i2c_client *client)
{
if (client == tas_client) {
driver_hooks->uninit(dev_get_drvdata(&client->dev));
i2c_detach_client(client);
kfree(client);
}
return 0;
}
void
tas_cleanup(void)
{
i2c_del_driver(&tas_driver);
}
int __init
tas_init(int driver_id, const char *driver_name)
{
u32* paddr;
printk(KERN_INFO "tas driver [%s])\n", driver_name);
#ifndef CONFIG_I2C_KEYWEST
request_module("i2c-keywest");
#endif
tas_node = find_devices("deq");
if (tas_node == NULL)
return -ENODEV;
paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
if (paddr) {
tas_i2c_address = (*paddr) >> 1;
printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
tas_i2c_address);
} else
printk(KERN_INFO "using i2c address: 0x%x (default)\n",
tas_i2c_address);
return i2c_add_driver(&tas_driver);
}

View File

@@ -0,0 +1,284 @@
#ifndef _TAS_COMMON_H_
#define _TAS_COMMON_H_
#include <linux/i2c.h>
#include <linux/soundcard.h>
#include <asm/string.h>
#define I2C_DRIVERID_TAS_BASE (0xFEBA)
#define SET_4_20(shadow, offset, val) \
do { \
(shadow)[(offset)+0] = ((val) >> 16) & 0xff; \
(shadow)[(offset)+1] = ((val) >> 8) & 0xff; \
(shadow)[(offset)+2] = ((val) >> 0) & 0xff; \
} while (0)
#define GET_4_20(shadow, offset) \
(((u_int)((shadow)[(offset)+0]) << 16) | \
((u_int)((shadow)[(offset)+1]) << 8) | \
((u_int)((shadow)[(offset)+2]) << 0))
#define TAS_BIQUAD_FAST_LOAD 0x01
#define TAS_DRCE_ENABLE 0x01
#define TAS_DRCE_ABOVE_RATIO 0x02
#define TAS_DRCE_BELOW_RATIO 0x04
#define TAS_DRCE_THRESHOLD 0x08
#define TAS_DRCE_ENERGY 0x10
#define TAS_DRCE_ATTACK 0x20
#define TAS_DRCE_DECAY 0x40
#define TAS_DRCE_ALL 0x7f
#define TAS_OUTPUT_HEADPHONES 0x00
#define TAS_OUTPUT_INTERNAL_SPKR 0x01
#define TAS_OUTPUT_EXTERNAL_SPKR 0x02
union tas_biquad_t {
struct {
int b0,b1,b2,a1,a2;
} coeff;
int buf[5];
};
struct tas_biquad_ctrl_t {
u_int channel:4;
u_int filter:4;
union tas_biquad_t data;
};
struct tas_biquad_ctrl_list_t {
int flags;
int filter_count;
struct tas_biquad_ctrl_t biquads[0];
};
struct tas_ratio_t {
unsigned short val; /* 8.8 */
unsigned short expand; /* 0 = compress, !0 = expand. */
};
struct tas_drce_t {
unsigned short enable;
struct tas_ratio_t above;
struct tas_ratio_t below;
short threshold; /* dB, 8.8 signed */
unsigned short energy; /* seconds, 4.12 unsigned */
unsigned short attack; /* seconds, 4.12 unsigned */
unsigned short decay; /* seconds, 4.12 unsigned */
};
struct tas_drce_ctrl_t {
uint flags;
struct tas_drce_t data;
};
struct tas_gain_t
{
unsigned int *master;
unsigned int *treble;
unsigned int *bass;
unsigned int *mixer;
};
typedef char tas_shadow_t[0x45];
struct tas_data_t
{
struct i2c_client *client;
tas_shadow_t *shadow;
uint mixer[SOUND_MIXER_NRDEVICES];
};
typedef int (*tas_hook_init_t)(struct i2c_client *);
typedef int (*tas_hook_post_init_t)(struct tas_data_t *);
typedef void (*tas_hook_uninit_t)(struct tas_data_t *);
typedef int (*tas_hook_get_mixer_level_t)(struct tas_data_t *,int,uint *);
typedef int (*tas_hook_set_mixer_level_t)(struct tas_data_t *,int,uint);
typedef int (*tas_hook_enter_sleep_t)(struct tas_data_t *);
typedef int (*tas_hook_leave_sleep_t)(struct tas_data_t *);
typedef int (*tas_hook_supported_mixers_t)(struct tas_data_t *);
typedef int (*tas_hook_mixer_is_stereo_t)(struct tas_data_t *,int);
typedef int (*tas_hook_stereo_mixers_t)(struct tas_data_t *);
typedef int (*tas_hook_output_device_change_t)(struct tas_data_t *,int,int,int);
typedef int (*tas_hook_device_ioctl_t)(struct tas_data_t *,u_int,u_long);
struct tas_driver_hooks_t {
/*
* All hardware initialisation must be performed in
* post_init(), as tas_dmasound_init() does a hardware reset.
*
* init() is called before tas_dmasound_init() so that
* ouput_device_change() is always called after i2c driver
* initialisation. The implication is that
* output_device_change() must cope with the fact that it
* may be called before post_init().
*/
tas_hook_init_t init;
tas_hook_post_init_t post_init;
tas_hook_uninit_t uninit;
tas_hook_get_mixer_level_t get_mixer_level;
tas_hook_set_mixer_level_t set_mixer_level;
tas_hook_enter_sleep_t enter_sleep;
tas_hook_leave_sleep_t leave_sleep;
tas_hook_supported_mixers_t supported_mixers;
tas_hook_mixer_is_stereo_t mixer_is_stereo;
tas_hook_stereo_mixers_t stereo_mixers;
tas_hook_output_device_change_t output_device_change;
tas_hook_device_ioctl_t device_ioctl;
};
enum tas_write_mode_t {
WRITE_HW = 0x01,
WRITE_SHADOW = 0x02,
WRITE_NORMAL = 0x03,
FORCE_WRITE = 0x04
};
static inline uint
tas_mono_to_stereo(uint mono)
{
mono &=0xff;
return mono | (mono<<8);
}
/*
* Todo: make these functions a bit more efficient !
*/
static inline int
tas_write_register( struct tas_data_t *self,
uint reg_num,
uint reg_width,
char *data,
uint write_mode)
{
int rc;
if (reg_width==0 || data==NULL || self==NULL)
return -EINVAL;
if (!(write_mode & FORCE_WRITE) &&
!memcmp(data,self->shadow[reg_num],reg_width))
return 0;
if (write_mode & WRITE_SHADOW)
memcpy(self->shadow[reg_num],data,reg_width);
if (write_mode & WRITE_HW) {
rc=i2c_smbus_write_block_data(self->client,
reg_num,
reg_width,
data);
if (rc < 0) {
printk("tas: I2C block write failed \n");
return rc;
}
}
return 0;
}
static inline int
tas_sync_register( struct tas_data_t *self,
uint reg_num,
uint reg_width)
{
int rc;
if (reg_width==0 || self==NULL)
return -EINVAL;
rc=i2c_smbus_write_block_data(self->client,
reg_num,
reg_width,
self->shadow[reg_num]);
if (rc < 0) {
printk("tas: I2C block write failed \n");
return rc;
}
return 0;
}
static inline int
tas_write_byte_register( struct tas_data_t *self,
uint reg_num,
char data,
uint write_mode)
{
if (self==NULL)
return -1;
if (!(write_mode & FORCE_WRITE) && data != self->shadow[reg_num][0])
return 0;
if (write_mode & WRITE_SHADOW)
self->shadow[reg_num][0]=data;
if (write_mode & WRITE_HW) {
if (i2c_smbus_write_byte_data(self->client, reg_num, data) < 0) {
printk("tas: I2C byte write failed \n");
return -1;
}
}
return 0;
}
static inline int
tas_sync_byte_register( struct tas_data_t *self,
uint reg_num,
uint reg_width)
{
if (reg_width==0 || self==NULL)
return -1;
if (i2c_smbus_write_byte_data(
self->client, reg_num, self->shadow[reg_num][0]) < 0) {
printk("tas: I2C byte write failed \n");
return -1;
}
return 0;
}
static inline int
tas_read_register( struct tas_data_t *self,
uint reg_num,
uint reg_width,
char *data)
{
if (reg_width==0 || data==NULL || self==NULL)
return -1;
memcpy(data,self->shadow[reg_num],reg_width);
return 0;
}
extern int tas_register_driver(struct tas_driver_hooks_t *hooks);
extern int tas_get_mixer_level(int mixer,uint *level);
extern int tas_set_mixer_level(int mixer,uint level);
extern int tas_enter_sleep(void);
extern int tas_leave_sleep(void);
extern int tas_supported_mixers(void);
extern int tas_mixer_is_stereo(int mixer);
extern int tas_stereo_mixers(void);
extern int tas_output_device_change(int,int,int);
extern int tas_device_ioctl(u_int, u_long);
extern void tas_cleanup(void);
extern int tas_init(int driver_id,const char *driver_name);
extern int tas_post_init(void);
#endif /* _TAS_COMMON_H_ */
/*
* Local Variables:
* tab-width: 8
* indent-tabs-mode: t
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,24 @@
#ifndef _TAS_EQ_PREFS_H_
#define _TAS_EQ_PREFS_H_
struct tas_eq_pref_t {
u_int sample_rate;
u_int device_id;
u_int output_id;
u_int speaker_id;
struct tas_drce_t *drce;
u_int filter_count;
struct tas_biquad_ctrl_t *biquads;
};
#endif /* _TAS_EQ_PREFS_H_ */
/*
* Local Variables:
* tab-width: 8
* indent-tabs-mode: t
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,24 @@
#ifndef _TAS_IOCTL_H_
#define _TAS_IOCTL_H_
#include <linux/i2c.h>
#include <linux/soundcard.h>
#define TAS_READ_EQ _SIOR('t',0,struct tas_biquad_ctrl_t)
#define TAS_WRITE_EQ _SIOW('t',0,struct tas_biquad_ctrl_t)
#define TAS_READ_EQ_LIST _SIOR('t',1,struct tas_biquad_ctrl_t)
#define TAS_WRITE_EQ_LIST _SIOW('t',1,struct tas_biquad_ctrl_t)
#define TAS_READ_EQ_FILTER_COUNT _SIOR('t',2,int)
#define TAS_READ_EQ_CHANNEL_COUNT _SIOR('t',3,int)
#define TAS_READ_DRCE _SIOR('t',4,struct tas_drce_ctrl_t)
#define TAS_WRITE_DRCE _SIOW('t',4,struct tas_drce_ctrl_t)
#define TAS_READ_DRCE_CAPS _SIOR('t',5,int)
#define TAS_READ_DRCE_MIN _SIOR('t',6,int)
#define TAS_READ_DRCE_MAX _SIOR('t',7,int)
#endif

View File

@@ -0,0 +1,897 @@
/*
* linux/sound/oss/dmasound/trans_16.c
*
* 16 bit translation routines. Only used by Power mac at present.
*
* See linux/sound/oss/dmasound/dmasound_core.c for copyright and
* history prior to 08/02/2001.
*
* 08/02/2001 Iain Sandoe
* split from dmasound_awacs.c
* 11/29/2003 Renzo Davoli (King Enzo)
* - input resampling (for soft rate < hard rate)
* - software line in gain control
*/
#include <linux/soundcard.h>
#include <asm/uaccess.h>
#include "dmasound.h"
static short dmasound_alaw2dma16[] ;
static short dmasound_ulaw2dma16[] ;
static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
/*** Translations ************************************************************/
static int expand_data; /* Data for expanding */
static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *table = dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
if (!stereo) {
short __user *up = (short __user *) userPtr;
while (count > 0) {
short data;
if (get_user(data, up++))
return -EFAULT;
*fp++ = data;
*fp++ = data;
count--;
}
} else {
if (copy_from_user(fp, userPtr, count * 4))
return -EFAULT;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
short data;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
*fp++ = data;
if (stereo) {
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
}
*fp++ = data;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned short *table = (unsigned short *)
(dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
unsigned int data = expand_data;
unsigned int *p = (unsigned int *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
int stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = table[c];
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + table[c];
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + (c << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = (c ^ 0x80) << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + ((c ^ 0x80) << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + c;
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + (c ^ mask);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
/* data in routines... */
static ssize_t pmac_ct_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = val >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
short data;
data = *fp++;
data = (data * software_input_volume) >> 7;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
data = (data * software_input_volume) >> 7;
if (put_user(data, up++))
return -EFAULT;
}
fp++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
while (count > 0) {
int data;
data = *fp++;
data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
data = (data * software_input_volume) >> 7;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
}
fp++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
/* data in routines (reducing speed)... */
static ssize_t pmac_ctx_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *p = (short *) &frame[*frameUsed];
int bal = expand_read_bal;
int vall,valr, stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char data;
if (bal<0 && userCount == 0)
break;
vall = *p++;
vall = (vall * software_input_volume) >> 7;
if (stereo) {
valr = *p;
valr = (valr * software_input_volume) >> 7;
}
p++;
if (bal < 0) {
data = vall >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = valr >> 8;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *p = (short *) &frame[*frameUsed];
int bal = expand_read_bal;
int vall,valr, stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char data;
if (bal<0 && userCount == 0)
break;
vall = *p++;
vall = (vall * software_input_volume) >> 7;
if (stereo) {
valr = *p;
valr = (valr * software_input_volume) >> 7;
}
p++;
if (bal < 0) {
data = (vall >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = (valr >> 8) ^ 0x80;
if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
int datal,datar;
if (bal<0 && userCount == 0)
break;
datal = *fp++;
datal = (datal * software_input_volume) >> 7;
if (stereo) {
datar = *fp;
datar = (datar * software_input_volume) >> 7;
}
fp++;
if (bal < 0) {
if (put_user(datal, up++))
return -EFAULT;
if (stereo) {
if (put_user(datar, up++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ctx_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
short *fp = (short *) &frame[*frameUsed];
short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
int datal,datar;
if (bal<0 && userCount == 0)
break;
datal = *fp++;
datal = (datal * software_input_volume) >> 7;
datal ^= mask;
if (stereo) {
datar = *fp;
datar = (datar * software_input_volume) >> 7;
datar ^= mask;
}
fp++;
if (bal < 0) {
if (put_user(datal, up++))
return -EFAULT;
if (stereo) {
if (put_user(datar, up++))
return -EFAULT;
}
userCount--;
bal += hSpeed;
}
frameLeft--;
bal -= sSpeed;
}
expand_read_bal=bal;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
TRANS transAwacsNormal = {
.ct_ulaw= pmac_ct_law,
.ct_alaw= pmac_ct_law,
.ct_s8= pmac_ct_s8,
.ct_u8= pmac_ct_u8,
.ct_s16be= pmac_ct_s16,
.ct_u16be= pmac_ct_u16,
.ct_s16le= pmac_ct_s16,
.ct_u16le= pmac_ct_u16,
};
TRANS transAwacsExpand = {
.ct_ulaw= pmac_ctx_law,
.ct_alaw= pmac_ctx_law,
.ct_s8= pmac_ctx_s8,
.ct_u8= pmac_ctx_u8,
.ct_s16be= pmac_ctx_s16,
.ct_u16be= pmac_ctx_u16,
.ct_s16le= pmac_ctx_s16,
.ct_u16le= pmac_ctx_u16,
};
TRANS transAwacsNormalRead = {
.ct_s8= pmac_ct_s8_read,
.ct_u8= pmac_ct_u8_read,
.ct_s16be= pmac_ct_s16_read,
.ct_u16be= pmac_ct_u16_read,
.ct_s16le= pmac_ct_s16_read,
.ct_u16le= pmac_ct_u16_read,
};
TRANS transAwacsExpandRead = {
.ct_s8= pmac_ctx_s8_read,
.ct_u8= pmac_ctx_u8_read,
.ct_s16be= pmac_ctx_s16_read,
.ct_u16be= pmac_ctx_u16_read,
.ct_s16le= pmac_ctx_s16_read,
.ct_u16le= pmac_ctx_u16_read,
};
/* translation tables */
/* 16 bit mu-law */
static short dmasound_ulaw2dma16[] = {
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
-11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
-7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
-876, -844, -812, -780, -748, -716, -684, -652,
-620, -588, -556, -524, -492, -460, -428, -396,
-372, -356, -340, -324, -308, -292, -276, -260,
-244, -228, -212, -196, -180, -164, -148, -132,
-120, -112, -104, -96, -88, -80, -72, -64,
-56, -48, -40, -32, -24, -16, -8, 0,
32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
876, 844, 812, 780, 748, 716, 684, 652,
620, 588, 556, 524, 492, 460, 428, 396,
372, 356, 340, 324, 308, 292, 276, 260,
244, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64,
56, 48, 40, 32, 24, 16, 8, 0,
};
/* 16 bit A-law */
static short dmasound_alaw2dma16[] = {
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
-30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
-11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472,
-15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
-344, -328, -376, -360, -280, -264, -312, -296,
-472, -456, -504, -488, -408, -392, -440, -424,
-88, -72, -120, -104, -24, -8, -56, -40,
-216, -200, -248, -232, -152, -136, -184, -168,
-1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-688, -656, -752, -720, -560, -528, -624, -592,
-944, -912, -1008, -976, -816, -784, -880, -848,
5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
344, 328, 376, 360, 280, 264, 312, 296,
472, 456, 504, 488, 408, 392, 440, 424,
88, 72, 120, 104, 24, 8, 56, 40,
216, 200, 248, 232, 152, 136, 184, 168,
1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
688, 656, 752, 720, 560, 528, 624, 592,
944, 912, 1008, 976, 816, 784, 880, 848,
};

737
sound/oss/emu10k1/8010.h Normal file
View File

@@ -0,0 +1,737 @@
/*
**********************************************************************
* 8010.h
* Copyright 1999-2001 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS
* line endings
* December 8, 1999 Jon Taylor Added lots of new register info
* May 16, 2001 Daniel Bertrand Added unofficial DBG register info
* Oct-Nov 2001 D.B. Added unofficial Audigy registers
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
*
**********************************************************************
*/
#ifndef _8010_H
#define _8010_H
#include <linux/types.h>
// Driver version:
#define MAJOR_VER 0
#define MINOR_VER 20
#define DRIVER_VERSION "0.20a"
// Audigy specify registers are prefixed with 'A_'
/************************************************************************************************/
/* PCI function 0 registers, address = <val> + PCIBASE0 */
/************************************************************************************************/
#define PTR 0x00 /* Indexed register set pointer register */
/* NOTE: The CHANNELNUM and ADDRESS words can */
/* be modified independently of each other. */
#define PTR_CHANNELNUM_MASK 0x0000003f /* For each per-channel register, indicates the */
/* channel number of the register to be */
/* accessed. For non per-channel registers the */
/* value should be set to zero. */
#define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */
#define DATA 0x04 /* Indexed register set data register */
#define IPR 0x08 /* Global interrupt pending register */
/* Clear pending interrupts by writing a 1 to */
/* the relevant bits and zero to the other bits */
/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */
#define A_IPR_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */
#define A_IPR_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */
#define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */
#define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */
#define IPR_FORCEINT 0x00400000 /* Force Sound Blaster interrupt */
#define IPR_PCIERROR 0x00200000 /* PCI bus error */
#define IPR_VOLINCR 0x00100000 /* Volume increment button pressed */
#define IPR_VOLDECR 0x00080000 /* Volume decrement button pressed */
#define IPR_MUTE 0x00040000 /* Mute button pressed */
#define IPR_MICBUFFULL 0x00020000 /* Microphone buffer full */
#define IPR_MICBUFHALFFULL 0x00010000 /* Microphone buffer half full */
#define IPR_ADCBUFFULL 0x00008000 /* ADC buffer full */
#define IPR_ADCBUFHALFFULL 0x00004000 /* ADC buffer half full */
#define IPR_EFXBUFFULL 0x00002000 /* Effects buffer full */
#define IPR_EFXBUFHALFFULL 0x00001000 /* Effects buffer half full */
#define IPR_GPSPDIFSTATUSCHANGE 0x00000800 /* GPSPDIF channel status change */
#define IPR_CDROMSTATUSCHANGE 0x00000400 /* CD-ROM channel status change */
#define IPR_INTERVALTIMER 0x00000200 /* Interval timer terminal count */
#define IPR_MIDITRANSBUFEMPTY 0x00000100 /* MIDI UART transmit buffer empty */
#define IPR_MIDIRECVBUFEMPTY 0x00000080 /* MIDI UART receive buffer empty */
#define IPR_CHANNELLOOP 0x00000040 /* One or more channel loop interrupts pending */
#define IPR_CHANNELNUMBERMASK 0x0000003f /* When IPR_CHANNELLOOP is set, indicates the */
/* Highest set channel in CLIPL or CLIPH. When */
/* IP is written with CL set, the bit in CLIPL */
/* or CLIPH corresponding to the CIN value */
/* written will be cleared. */
#define A_IPR_MIDITRANSBUFEMPTY1 IPR_MIDITRANSBUFEMPTY /* MIDI UART transmit buffer empty */
#define A_IPR_MIDIRECVBUFEMPTY1 IPR_MIDIRECVBUFEMPTY /* MIDI UART receive buffer empty */
#define INTE 0x0c /* Interrupt enable register */
#define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */
#define INTE_VIRTUALSB_220 0x00000000 /* Capture at I/O base address 0x220-0x22f */
#define INTE_VIRTUALSB_240 0x40000000 /* Capture at I/O base address 0x240 */
#define INTE_VIRTUALSB_260 0x80000000 /* Capture at I/O base address 0x260 */
#define INTE_VIRTUALSB_280 0xc0000000 /* Capture at I/O base address 0x280 */
#define INTE_VIRTUALMPU_MASK 0x30000000 /* Virtual MPU I/O port capture */
#define INTE_VIRTUALMPU_300 0x00000000 /* Capture at I/O base address 0x300-0x301 */
#define INTE_VIRTUALMPU_310 0x10000000 /* Capture at I/O base address 0x310 */
#define INTE_VIRTUALMPU_320 0x20000000 /* Capture at I/O base address 0x320 */
#define INTE_VIRTUALMPU_330 0x30000000 /* Capture at I/O base address 0x330 */
#define INTE_MASTERDMAENABLE 0x08000000 /* Master DMA emulation at 0x000-0x00f */
#define INTE_SLAVEDMAENABLE 0x04000000 /* Slave DMA emulation at 0x0c0-0x0df */
#define INTE_MASTERPICENABLE 0x02000000 /* Master PIC emulation at 0x020-0x021 */
#define INTE_SLAVEPICENABLE 0x01000000 /* Slave PIC emulation at 0x0a0-0x0a1 */
#define INTE_VSBENABLE 0x00800000 /* Enable virtual Soundblaster */
#define INTE_ADLIBENABLE 0x00400000 /* Enable AdLib emulation at 0x388-0x38b */
#define INTE_MPUENABLE 0x00200000 /* Enable virtual MPU */
#define INTE_FORCEINT 0x00100000 /* Continuously assert INTAN */
#define INTE_MRHANDENABLE 0x00080000 /* Enable the "Mr. Hand" logic */
/* NOTE: There is no reason to use this under */
/* Linux, and it will cause odd hardware */
/* behavior and possibly random segfaults and */
/* lockups if enabled. */
/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */
#define A_INTE_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */
#define A_INTE_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */
#define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */
/* NOTE: This bit must always be enabled */
#define INTE_FXDSPENABLE 0x00001000 /* Enable FX DSP interrupts */
#define INTE_PCIERRORENABLE 0x00000800 /* Enable PCI bus error interrupts */
#define INTE_VOLINCRENABLE 0x00000400 /* Enable volume increment button interrupts */
#define INTE_VOLDECRENABLE 0x00000200 /* Enable volume decrement button interrupts */
#define INTE_MUTEENABLE 0x00000100 /* Enable mute button interrupts */
#define INTE_MICBUFENABLE 0x00000080 /* Enable microphone buffer interrupts */
#define INTE_ADCBUFENABLE 0x00000040 /* Enable ADC buffer interrupts */
#define INTE_EFXBUFENABLE 0x00000020 /* Enable Effects buffer interrupts */
#define INTE_GPSPDIFENABLE 0x00000010 /* Enable GPSPDIF status interrupts */
#define INTE_CDSPDIFENABLE 0x00000008 /* Enable CDSPDIF status interrupts */
#define INTE_INTERVALTIMERENB 0x00000004 /* Enable interval timer interrupts */
#define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */
#define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */
/* The next two interrupts are for the midi port on the Audigy (A_MPU2) */
#define A_INTE_MIDITXENABLE1 INTE_MIDITXENABLE
#define A_INTE_MIDIRXENABLE1 INTE_MIDIRXENABLE
#define WC 0x10 /* Wall Clock register */
#define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */
#define WC_SAMPLECOUNTER 0x14060010
#define WC_CURRENTCHANNEL 0x0000003F /* Channel [0..63] currently being serviced */
/* NOTE: Each channel takes 1/64th of a sample */
/* period to be serviced. */
#define HCFG 0x14 /* Hardware config register */
/* NOTE: There is no reason to use the legacy */
/* SoundBlaster emulation stuff described below */
/* under Linux, and all kinds of weird hardware */
/* behavior can result if you try. Don't. */
#define HCFG_LEGACYFUNC_MASK 0xe0000000 /* Legacy function number */
#define HCFG_LEGACYFUNC_MPU 0x00000000 /* Legacy MPU */
#define HCFG_LEGACYFUNC_SB 0x40000000 /* Legacy SB */
#define HCFG_LEGACYFUNC_AD 0x60000000 /* Legacy AD */
#define HCFG_LEGACYFUNC_MPIC 0x80000000 /* Legacy MPIC */
#define HCFG_LEGACYFUNC_MDMA 0xa0000000 /* Legacy MDMA */
#define HCFG_LEGACYFUNC_SPCI 0xc0000000 /* Legacy SPCI */
#define HCFG_LEGACYFUNC_SDMA 0xe0000000 /* Legacy SDMA */
#define HCFG_IOCAPTUREADDR 0x1f000000 /* The 4 LSBs of the captured I/O address. */
#define HCFG_LEGACYWRITE 0x00800000 /* 1 = write, 0 = read */
#define HCFG_LEGACYWORD 0x00400000 /* 1 = word, 0 = byte */
#define HCFG_LEGACYINT 0x00200000 /* 1 = legacy event captured. Write 1 to clear. */
/* NOTE: The rest of the bits in this register */
/* _are_ relevant under Linux. */
#define HCFG_CODECFORMAT_MASK 0x00070000 /* CODEC format */
#define HCFG_CODECFORMAT_AC97 0x00000000 /* AC97 CODEC format -- Primary Output */
#define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */
#define HCFG_GPINPUT0 0x00004000 /* External pin112 */
#define HCFG_GPINPUT1 0x00002000 /* External pin110 */
#define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */
#define HCFG_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */
#define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */
#define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */
/* 1 = Force all 3 async digital inputs to use */
/* the same async sample rate tracker (ZVIDEO) */
#define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */
#define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */
#define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */
#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */
#define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */
/* will automatically mute their output when */
/* they are not rate-locked to the external */
/* async audio source */
#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */
/* NOTE: This should generally never be used. */
#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */
/* NOTE: This should generally never be used. */
#define HCFG_LOCKTANKCACHE 0x01020014
#define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */
/* NOTE: This is a 'cheap' way to implement a */
/* master mute function on the mute button, and */
/* in general should not be used unless a more */
/* sophisticated master mute function has not */
/* been written. */
#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
/* Should be set to 1 when the EMU10K1 is */
/* completely initialized. */
//For Audigy, MPU port move to 0x70-0x74 ptr register
#define MUDATA 0x18 /* MPU401 data register (8 bits) */
#define MUCMD 0x19 /* MPU401 command register (8 bits) */
#define MUCMD_RESET 0xff /* RESET command */
#define MUCMD_ENTERUARTMODE 0x3f /* Enter_UART_mode command */
/* NOTE: All other commands are ignored */
#define MUSTAT MUCMD /* MPU401 status register (8 bits) */
#define MUSTAT_IRDYN 0x80 /* 0 = MIDI data or command ACK */
#define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */
#define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */
#define A_GPINPUT_MASK 0xff00
#define A_GPOUTPUT_MASK 0x00ff
#define TIMER 0x1a /* Timer terminal count register (16-bit) */
/* NOTE: After the rate is changed, a maximum */
/* of 1024 sample periods should be allowed */
/* before the new rate is guaranteed accurate. */
#define TIMER_RATE_MASK 0x03ff /* Timer interrupt rate in sample periods */
/* 0 == 1024 periods, [1..4] are not useful */
#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
#define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */
#define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */
/********************************************************************************************************/
/* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */
/********************************************************************************************************/
#define CPF 0x00 /* Current pitch and fraction register */
#define CPF_CURRENTPITCH_MASK 0xffff0000 /* Current pitch (linear, 0x4000 == unity pitch shift) */
#define CPF_CURRENTPITCH 0x10100000
#define CPF_STEREO_MASK 0x00008000 /* 1 = Even channel interleave, odd channel locked */
#define CPF_STOP_MASK 0x00004000 /* 1 = Current pitch forced to 0 */
#define CPF_FRACADDRESS_MASK 0x00003fff /* Linear fractional address of the current channel */
#define PTRX 0x01 /* Pitch target and send A/B amounts register */
#define PTRX_PITCHTARGET_MASK 0xffff0000 /* Pitch target of specified channel */
#define PTRX_PITCHTARGET 0x10100001
#define PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 /* Linear level of channel output sent to FX send bus A */
#define PTRX_FXSENDAMOUNT_A 0x08080001
#define PTRX_FXSENDAMOUNT_B_MASK 0x000000ff /* Linear level of channel output sent to FX send bus B */
#define PTRX_FXSENDAMOUNT_B 0x08000001
#define CVCF 0x02 /* Current volume and filter cutoff register */
#define CVCF_CURRENTVOL_MASK 0xffff0000 /* Current linear volume of specified channel */
#define CVCF_CURRENTVOL 0x10100002
#define CVCF_CURRENTFILTER_MASK 0x0000ffff /* Current filter cutoff frequency of specified channel */
#define CVCF_CURRENTFILTER 0x10000002
#define VTFT 0x03 /* Volume target and filter cutoff target register */
#define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */
#define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */
#define Z1 0x05 /* Filter delay memory 1 register */
#define Z2 0x04 /* Filter delay memory 2 register */
#define PSST 0x06 /* Send C amount and loop start address register */
#define PSST_FXSENDAMOUNT_C_MASK 0xff000000 /* Linear level of channel output sent to FX send bus C */
#define PSST_FXSENDAMOUNT_C 0x08180006
#define PSST_LOOPSTARTADDR_MASK 0x00ffffff /* Loop start address of the specified channel */
#define PSST_LOOPSTARTADDR 0x18000006
#define DSL 0x07 /* Send D amount and loop start address register */
#define DSL_FXSENDAMOUNT_D_MASK 0xff000000 /* Linear level of channel output sent to FX send bus D */
#define DSL_FXSENDAMOUNT_D 0x08180007
#define DSL_LOOPENDADDR_MASK 0x00ffffff /* Loop end address of the specified channel */
#define DSL_LOOPENDADDR 0x18000007
#define CCCA 0x08 /* Filter Q, interp. ROM, byte size, cur. addr register */
#define CCCA_RESONANCE 0xf0000000 /* Lowpass filter resonance (Q) height */
#define CCCA_INTERPROMMASK 0x0e000000 /* Selects passband of interpolation ROM */
/* 1 == full band, 7 == lowpass */
/* ROM 0 is used when pitch shifting downward or less */
/* then 3 semitones upward. Increasingly higher ROM */
/* numbers are used, typically in steps of 3 semitones, */
/* as upward pitch shifting is performed. */
#define CCCA_INTERPROM_0 0x00000000 /* Select interpolation ROM 0 */
#define CCCA_INTERPROM_1 0x02000000 /* Select interpolation ROM 1 */
#define CCCA_INTERPROM_2 0x04000000 /* Select interpolation ROM 2 */
#define CCCA_INTERPROM_3 0x06000000 /* Select interpolation ROM 3 */
#define CCCA_INTERPROM_4 0x08000000 /* Select interpolation ROM 4 */
#define CCCA_INTERPROM_5 0x0a000000 /* Select interpolation ROM 5 */
#define CCCA_INTERPROM_6 0x0c000000 /* Select interpolation ROM 6 */
#define CCCA_INTERPROM_7 0x0e000000 /* Select interpolation ROM 7 */
#define CCCA_8BITSELECT 0x01000000 /* 1 = Sound memory for this channel uses 8-bit samples */
#define CCCA_CURRADDR_MASK 0x00ffffff /* Current address of the selected channel */
#define CCCA_CURRADDR 0x18000008
#define CCR 0x09 /* Cache control register */
#define CCR_CACHEINVALIDSIZE 0x07190009
#define CCR_CACHEINVALIDSIZE_MASK 0xfe000000 /* Number of invalid samples cache for this channel */
#define CCR_CACHELOOPFLAG 0x01000000 /* 1 = Cache has a loop service pending */
#define CCR_INTERLEAVEDSAMPLES 0x00800000 /* 1 = A cache service will fetch interleaved samples */
#define CCR_WORDSIZEDSAMPLES 0x00400000 /* 1 = A cache service will fetch word sized samples */
#define CCR_READADDRESS 0x06100009
#define CCR_READADDRESS_MASK 0x003f0000 /* Location of cache just beyond current cache service */
#define CCR_LOOPINVALSIZE 0x0000fe00 /* Number of invalid samples in cache prior to loop */
/* NOTE: This is valid only if CACHELOOPFLAG is set */
#define CCR_LOOPFLAG 0x00000100 /* Set for a single sample period when a loop occurs */
#define CCR_CACHELOOPADDRHI 0x000000ff /* DSL_LOOPSTARTADDR's hi byte if CACHELOOPFLAG is set */
#define CLP 0x0a /* Cache loop register (valid if CCR_CACHELOOPFLAG = 1) */
/* NOTE: This register is normally not used */
#define CLP_CACHELOOPADDR 0x0000ffff /* Cache loop address (DSL_LOOPSTARTADDR [0..15]) */
#define FXRT 0x0b /* Effects send routing register */
/* NOTE: It is illegal to assign the same routing to */
/* two effects sends. */
#define FXRT_CHANNELA 0x000f0000 /* Effects send bus number for channel's effects send A */
#define FXRT_CHANNELB 0x00f00000 /* Effects send bus number for channel's effects send B */
#define FXRT_CHANNELC 0x0f000000 /* Effects send bus number for channel's effects send C */
#define FXRT_CHANNELD 0xf0000000 /* Effects send bus number for channel's effects send D */
#define MAPA 0x0c /* Cache map A */
#define MAPB 0x0d /* Cache map B */
#define MAP_PTE_MASK 0xffffe000 /* The 19 MSBs of the PTE indexed by the PTI */
#define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */
#define ENVVOL 0x10 /* Volume envelope register */
#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */
/* 0x8000-n == 666*n usec delay */
#define ATKHLDV 0x11 /* Volume envelope hold and attack register */
#define ATKHLDV_PHASE0 0x00008000 /* 0 = Begin attack phase */
#define ATKHLDV_HOLDTIME_MASK 0x00007f00 /* Envelope hold time (127-n == n*88.2msec) */
#define ATKHLDV_ATTACKTIME_MASK 0x0000007f /* Envelope attack time, log encoded */
/* 0 = infinite, 1 = 10.9msec, ... 0x7f = 5.5msec */
#define DCYSUSV 0x12 /* Volume envelope sustain and decay register */
#define DCYSUSV_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */
#define DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */
#define DCYSUSV_CHANNELENABLE_MASK 0x00000080 /* 1 = Inhibit envelope engine from writing values in */
/* this channel and from writing to pitch, filter and */
/* volume targets. */
#define DCYSUSV_DECAYTIME_MASK 0x0000007f /* Volume envelope decay time, log encoded */
/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */
#define LFOVAL1 0x13 /* Modulation LFO value */
#define LFOVAL_MASK 0x0000ffff /* Current value of modulation LFO state variable */
/* 0x8000-n == 666*n usec delay */
#define ENVVAL 0x14 /* Modulation envelope register */
#define ENVVAL_MASK 0x0000ffff /* Current value of modulation envelope state variable */
/* 0x8000-n == 666*n usec delay */
#define ATKHLDM 0x15 /* Modulation envelope hold and attack register */
#define ATKHLDM_PHASE0 0x00008000 /* 0 = Begin attack phase */
#define ATKHLDM_HOLDTIME 0x00007f00 /* Envelope hold time (127-n == n*42msec) */
#define ATKHLDM_ATTACKTIME 0x0000007f /* Envelope attack time, log encoded */
/* 0 = infinite, 1 = 11msec, ... 0x7f = 5.5msec */
#define DCYSUSM 0x16 /* Modulation envelope decay and sustain register */
#define DCYSUSM_PHASE1_MASK 0x00008000 /* 0 = Begin attack phase, 1 = begin release phase */
#define DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 /* 127 = full, 0 = off, 0.75dB increments */
#define DCYSUSM_DECAYTIME_MASK 0x0000007f /* Envelope decay time, log encoded */
/* 0 = 43.7msec, 1 = 21.8msec, 0x7f = 22msec */
#define LFOVAL2 0x17 /* Vibrato LFO register */
#define LFOVAL2_MASK 0x0000ffff /* Current value of vibrato LFO state variable */
/* 0x8000-n == 666*n usec delay */
#define IP 0x18 /* Initial pitch register */
#define IP_MASK 0x0000ffff /* Exponential initial pitch shift */
/* 4 bits of octave, 12 bits of fractional octave */
#define IP_UNITY 0x0000e000 /* Unity pitch shift */
#define IFATN 0x19 /* Initial filter cutoff and attenuation register */
#define IFATN_FILTERCUTOFF_MASK 0x0000ff00 /* Initial filter cutoff frequency in exponential units */
/* 6 most significant bits are semitones */
/* 2 least significant bits are fractions */
#define IFATN_FILTERCUTOFF 0x08080019
#define IFATN_ATTENUATION_MASK 0x000000ff /* Initial attenuation in 0.375dB steps */
#define IFATN_ATTENUATION 0x08000019
#define PEFE 0x1a /* Pitch envelope and filter envelope amount register */
#define PEFE_PITCHAMOUNT_MASK 0x0000ff00 /* Pitch envlope amount */
/* Signed 2's complement, +/- one octave peak extremes */
#define PEFE_PITCHAMOUNT 0x0808001a
#define PEFE_FILTERAMOUNT_MASK 0x000000ff /* Filter envlope amount */
/* Signed 2's complement, +/- six octaves peak extremes */
#define PEFE_FILTERAMOUNT 0x0800001a
#define FMMOD 0x1b /* Vibrato/filter modulation from LFO register */
#define FMMOD_MODVIBRATO 0x0000ff00 /* Vibrato LFO modulation depth */
/* Signed 2's complement, +/- one octave extremes */
#define FMMOD_MOFILTER 0x000000ff /* Filter LFO modulation depth */
/* Signed 2's complement, +/- three octave extremes */
#define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */
#define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */
/* Signed 2's complement, with +/- 12dB extremes */
#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */
/* ??Hz steps, maximum of ?? Hz. */
#define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */
#define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */
/* Signed 2's complement, +/- one octave extremes */
#define FM2FRQ2_FREQUENCY 0x000000ff /* Vibrato LFO frequency */
/* 0.039Hz steps, maximum of 9.85 Hz. */
#define TEMPENV 0x1e /* Tempory envelope register */
#define TEMPENV_MASK 0x0000ffff /* 16-bit value */
/* NOTE: All channels contain internal variables; do */
/* not write to these locations. */
#define CD0 0x20 /* Cache data 0 register */
#define CD1 0x21 /* Cache data 1 register */
#define CD2 0x22 /* Cache data 2 register */
#define CD3 0x23 /* Cache data 3 register */
#define CD4 0x24 /* Cache data 4 register */
#define CD5 0x25 /* Cache data 5 register */
#define CD6 0x26 /* Cache data 6 register */
#define CD7 0x27 /* Cache data 7 register */
#define CD8 0x28 /* Cache data 8 register */
#define CD9 0x29 /* Cache data 9 register */
#define CDA 0x2a /* Cache data A register */
#define CDB 0x2b /* Cache data B register */
#define CDC 0x2c /* Cache data C register */
#define CDD 0x2d /* Cache data D register */
#define CDE 0x2e /* Cache data E register */
#define CDF 0x2f /* Cache data F register */
#define PTB 0x40 /* Page table base register */
#define PTB_MASK 0xfffff000 /* Physical address of the page table in host memory */
#define TCB 0x41 /* Tank cache base register */
#define TCB_MASK 0xfffff000 /* Physical address of the bottom of host based TRAM */
#define ADCCR 0x42 /* ADC sample rate/stereo control register */
#define ADCCR_RCHANENABLE 0x00000010 /* Enables right channel for writing to the host */
#define ADCCR_LCHANENABLE 0x00000008 /* Enables left channel for writing to the host */
/* NOTE: To guarantee phase coherency, both channels */
/* must be disabled prior to enabling both channels. */
#define A_ADCCR_RCHANENABLE 0x00000020
#define A_ADCCR_LCHANENABLE 0x00000010
#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */
#define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */
#define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */
#define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */
#define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */
#define ADCCR_SAMPLERATE_24 0x00000003 /* 24kHz sample rate */
#define ADCCR_SAMPLERATE_22 0x00000004 /* 22.05kHz sample rate */
#define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */
#define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */
#define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */
#define A_ADCCR_SAMPLERATE_12 0x00000006 /* 12kHz sample rate */
#define A_ADCCR_SAMPLERATE_11 0x00000007 /* 11.025kHz sample rate */
#define A_ADCCR_SAMPLERATE_8 0x00000008 /* 8kHz sample rate */
#define FXWC 0x43 /* FX output write channels register */
/* When set, each bit enables the writing of the */
/* corresponding FX output channel (internal registers */
/* 0x20-0x3f) into host memory. This mode of recording */
/* is 16bit, 48KHz only. All 32 channels can be enabled */
/* simultaneously. */
#define TCBS 0x44 /* Tank cache buffer size register */
#define TCBS_MASK 0x00000007 /* Tank cache buffer size field */
#define TCBS_BUFFSIZE_16K 0x00000000
#define TCBS_BUFFSIZE_32K 0x00000001
#define TCBS_BUFFSIZE_64K 0x00000002
#define TCBS_BUFFSIZE_128K 0x00000003
#define TCBS_BUFFSIZE_256K 0x00000004
#define TCBS_BUFFSIZE_512K 0x00000005
#define TCBS_BUFFSIZE_1024K 0x00000006
#define TCBS_BUFFSIZE_2048K 0x00000007
#define MICBA 0x45 /* AC97 microphone buffer address register */
#define MICBA_MASK 0xfffff000 /* 20 bit base address */
#define ADCBA 0x46 /* ADC buffer address register */
#define ADCBA_MASK 0xfffff000 /* 20 bit base address */
#define FXBA 0x47 /* FX Buffer Address */
#define FXBA_MASK 0xfffff000 /* 20 bit base address */
#define MICBS 0x49 /* Microphone buffer size register */
#define ADCBS 0x4a /* ADC buffer size register */
#define FXBS 0x4b /* FX buffer size register */
/* The following mask values define the size of the ADC, MIX and FX buffers in bytes */
#define ADCBS_BUFSIZE_NONE 0x00000000
#define ADCBS_BUFSIZE_384 0x00000001
#define ADCBS_BUFSIZE_448 0x00000002
#define ADCBS_BUFSIZE_512 0x00000003
#define ADCBS_BUFSIZE_640 0x00000004
#define ADCBS_BUFSIZE_768 0x00000005
#define ADCBS_BUFSIZE_896 0x00000006
#define ADCBS_BUFSIZE_1024 0x00000007
#define ADCBS_BUFSIZE_1280 0x00000008
#define ADCBS_BUFSIZE_1536 0x00000009
#define ADCBS_BUFSIZE_1792 0x0000000a
#define ADCBS_BUFSIZE_2048 0x0000000b
#define ADCBS_BUFSIZE_2560 0x0000000c
#define ADCBS_BUFSIZE_3072 0x0000000d
#define ADCBS_BUFSIZE_3584 0x0000000e
#define ADCBS_BUFSIZE_4096 0x0000000f
#define ADCBS_BUFSIZE_5120 0x00000010
#define ADCBS_BUFSIZE_6144 0x00000011
#define ADCBS_BUFSIZE_7168 0x00000012
#define ADCBS_BUFSIZE_8192 0x00000013
#define ADCBS_BUFSIZE_10240 0x00000014
#define ADCBS_BUFSIZE_12288 0x00000015
#define ADCBS_BUFSIZE_14366 0x00000016
#define ADCBS_BUFSIZE_16384 0x00000017
#define ADCBS_BUFSIZE_20480 0x00000018
#define ADCBS_BUFSIZE_24576 0x00000019
#define ADCBS_BUFSIZE_28672 0x0000001a
#define ADCBS_BUFSIZE_32768 0x0000001b
#define ADCBS_BUFSIZE_40960 0x0000001c
#define ADCBS_BUFSIZE_49152 0x0000001d
#define ADCBS_BUFSIZE_57344 0x0000001e
#define ADCBS_BUFSIZE_65536 0x0000001f
#define CDCS 0x50 /* CD-ROM digital channel status register */
#define GPSCS 0x51 /* General Purpose SPDIF channel status register*/
#define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
/* definitions for debug register - taken from the alsa drivers */
#define DBG_ZC 0x80000000 /* zero tram counter */
#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */
#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */
#define DBG_SINGLE_STEP 0x00008000 /* single step mode */
#define DBG_STEP 0x00004000 /* start single step */
#define DBG_CONDITION_CODE 0x00003e00 /* condition code */
#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */
#define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */
#define A_DBG 0x53
#define A_DBG_SINGLE_STEP 0x00020000 /* Set to zero to start dsp */
#define A_DBG_ZC 0x40000000 /* zero tram counter */
#define A_DBG_STEP_ADDR 0x000003ff
#define A_DBG_SATURATION_OCCURED 0x20000000
#define A_DBG_SATURATION_ADDR 0x0ffc0000
#define SPCS0 0x54 /* SPDIF output Channel Status 0 register */
#define SPCS1 0x55 /* SPDIF output Channel Status 1 register */
#define SPCS2 0x56 /* SPDIF output Channel Status 2 register */
#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */
#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */
#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */
#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */
#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */
#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */
#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */
#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */
#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */
#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */
#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */
#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */
#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */
#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */
#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */
#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */
#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */
#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */
#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */
#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */
#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */
#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */
#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */
/* The 32-bit CLIx and SOLx registers all have one bit per channel control/status */
#define CLIEL 0x58 /* Channel loop interrupt enable low register */
#define CLIEH 0x59 /* Channel loop interrupt enable high register */
#define CLIPL 0x5a /* Channel loop interrupt pending low register */
#define CLIPH 0x5b /* Channel loop interrupt pending high register */
#define SOLEL 0x5c /* Stop on loop enable low register */
#define SOLEH 0x5d /* Stop on loop enable high register */
#define SPBYPASS 0x5e /* SPDIF BYPASS mode register */
#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */
#define AC97SLOT 0x5f /* additional AC97 slots enable bits */
#define AC97SLOT_CNTR 0x10 /* Center enable */
#define AC97SLOT_LFE 0x20 /* LFE enable */
#define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */
#define GPSRCS 0x61 /* General Purpose SPDIF sample rate cvt status */
#define ZVSRCS 0x62 /* ZVideo sample rate converter status */
/* NOTE: This one has no SPDIFLOCKED field */
/* Assumes sample lock */
/* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS. */
#define SRCS_SPDIFLOCKED 0x02000000 /* SPDIF stream locked */
#define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */
#define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */
/* Note that these values can vary +/- by a small amount */
#define SRCS_SPDIFRATE_44 0x0003acd9
#define SRCS_SPDIFRATE_48 0x00040000
#define SRCS_SPDIFRATE_96 0x00080000
#define MICIDX 0x63 /* Microphone recording buffer index register */
#define MICIDX_MASK 0x0000ffff /* 16-bit value */
#define MICIDX_IDX 0x10000063
#define A_ADCIDX 0x63
#define A_ADCIDX_IDX 0x10000063
#define ADCIDX 0x64 /* ADC recording buffer index register */
#define ADCIDX_MASK 0x0000ffff /* 16 bit index field */
#define ADCIDX_IDX 0x10000064
#define FXIDX 0x65 /* FX recording buffer index register */
#define FXIDX_MASK 0x0000ffff /* 16-bit value */
#define FXIDX_IDX 0x10000065
/* This is the MPU port on the card (via the game port) */
#define A_MUDATA1 0x70
#define A_MUCMD1 0x71
#define A_MUSTAT1 A_MUCMD1
/* This is the MPU port on the Audigy Drive */
#define A_MUDATA2 0x72
#define A_MUCMD2 0x73
#define A_MUSTAT2 A_MUCMD2
/* The next two are the Audigy equivalent of FXWC */
/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */
/* Each bit selects a channel for recording */
#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */
#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */
#define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */
#define A_SPDIF_48000 0x00000080
#define A_SPDIF_44100 0x00000000
#define A_SPDIF_96000 0x00000040
#define A_FXRT2 0x7c
#define A_FXRT_CHANNELE 0x0000003f /* Effects send bus number for channel's effects send E */
#define A_FXRT_CHANNELF 0x00003f00 /* Effects send bus number for channel's effects send F */
#define A_FXRT_CHANNELG 0x003f0000 /* Effects send bus number for channel's effects send G */
#define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send H */
#define A_SENDAMOUNTS 0x7d
#define A_FXSENDAMOUNT_E_MASK 0xff000000
#define A_FXSENDAMOUNT_F_MASK 0x00ff0000
#define A_FXSENDAMOUNT_G_MASK 0x0000ff00
#define A_FXSENDAMOUNT_H_MASK 0x000000ff
/* The send amounts for this one are the same as used with the emu10k1 */
#define A_FXRT1 0x7e
#define A_FXRT_CHANNELA 0x0000003f
#define A_FXRT_CHANNELB 0x00003f00
#define A_FXRT_CHANNELC 0x003f0000
#define A_FXRT_CHANNELD 0x3f000000
/* Each FX general purpose register is 32 bits in length, all bits are used */
#define FXGPREGBASE 0x100 /* FX general purpose registers base */
#define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */
/* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */
/* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */
/* locations are for external TRAM. */
#define TANKMEMDATAREGBASE 0x200 /* Tank memory data registers base */
#define TANKMEMDATAREG_MASK 0x000fffff /* 20 bit tank audio data field */
/* Combined address field and memory opcode or flag field. 160 locations, last 32 are external */
#define TANKMEMADDRREGBASE 0x300 /* Tank memory address registers base */
#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */
#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */
#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */
#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */
#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */
#define MICROCODEBASE 0x400 /* Microcode data base address */
/* Each DSP microcode instruction is mapped into 2 doublewords */
/* NOTE: When writing, always write the LO doubleword first. Reads can be in either order. */
#define LOWORD_OPX_MASK 0x000ffc00 /* Instruction operand X */
#define LOWORD_OPY_MASK 0x000003ff /* Instruction operand Y */
#define HIWORD_OPCODE_MASK 0x00f00000 /* Instruction opcode */
#define HIWORD_RESULT_MASK 0x000ffc00 /* Instruction result */
#define HIWORD_OPA_MASK 0x000003ff /* Instruction operand A */
/* Audigy Soundcard have a different instruction format */
#define AUDIGY_CODEBASE 0x600
#define A_LOWORD_OPY_MASK 0x000007ff
#define A_LOWORD_OPX_MASK 0x007ff000
#define A_HIWORD_OPCODE_MASK 0x0f000000
#define A_HIWORD_RESULT_MASK 0x007ff000
#define A_HIWORD_OPA_MASK 0x000007ff
#endif /* _8010_H */

View File

@@ -0,0 +1,17 @@
# 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

1588
sound/oss/emu10k1/audio.c Normal file

File diff suppressed because it is too large Load Diff

44
sound/oss/emu10k1/audio.h Normal file
View File

@@ -0,0 +1,44 @@
/*
**********************************************************************
* audio.c -- /dev/dsp interface for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up types/leaks
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _AUDIO_H
#define _AUDIO_H
struct emu10k1_wavedevice
{
struct emu10k1_card *card;
struct wiinst *wiinst;
struct woinst *woinst;
u16 enablebits;
};
#endif /* _AUDIO_H */

832
sound/oss/emu10k1/cardmi.c Normal file
View File

@@ -0,0 +1,832 @@
/*
**********************************************************************
* sblive_mi.c - MIDI UART input HAL for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox clean up
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/slab.h>
#include <linux/jiffies.h>
#include "hwaccess.h"
#include "8010.h"
#include "cardmi.h"
#include "irqmgr.h"
static int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid);
static int sblive_miStateInit(struct emu10k1_mpuin *);
static int sblive_miStateEntry(struct emu10k1_mpuin *, u8);
static int sblive_miStateParse(struct emu10k1_mpuin *, u8);
static int sblive_miState3Byte(struct emu10k1_mpuin *, u8);
static int sblive_miState3ByteKey(struct emu10k1_mpuin *, u8);
static int sblive_miState3ByteVel(struct emu10k1_mpuin *, u8);
static int sblive_miState2Byte(struct emu10k1_mpuin *, u8);
static int sblive_miState2ByteKey(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysCommon2(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysCommon3(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysExNorm(struct emu10k1_mpuin *, u8);
static int sblive_miStateSysReal(struct emu10k1_mpuin *, u8);
static struct {
int (*Fn) (struct emu10k1_mpuin *, u8);
} midistatefn[] = {
{
sblive_miStateParse}, {
sblive_miState3Byte}, /* 0x8n, 0x9n, 0xAn, 0xBn, 0xEn */
{
sblive_miState3ByteKey}, /* Byte 1 */
{
sblive_miState3ByteVel}, /* Byte 2 */
{
sblive_miState2Byte}, /* 0xCn, 0xDn */
{
sblive_miState2ByteKey}, /* Byte 1 */
{
sblive_miStateSysCommon2}, /* 0xF1 , 0xF3 */
{
sblive_miStateSysCommon2Key}, /* 0xF1 , 0xF3, Byte 1 */
{
sblive_miStateSysCommon3}, /* 0xF2 */
{
sblive_miStateSysCommon3Key}, /* 0xF2 , Byte 1 */
{
sblive_miStateSysCommon3Vel}, /* 0xF2 , Byte 2 */
{
sblive_miStateSysExNorm}, /* 0xF0, 0xF7, Normal mode */
{
sblive_miStateSysReal} /* 0xF4 - 0xF6 ,0xF8 - 0xFF */
};
/* Installs the IRQ handler for the MPU in port */
/* and initialize parameters */
int emu10k1_mpuin_open(struct emu10k1_card *card, struct midi_openinfo *openinfo)
{
struct emu10k1_mpuin *card_mpuin = card->mpuin;
DPF(2, "emu10k1_mpuin_open\n");
if (!(card_mpuin->status & FLAGS_AVAILABLE))
return -1;
/* Copy open info and mark channel as in use */
card_mpuin->openinfo = *openinfo;
card_mpuin->status &= ~FLAGS_AVAILABLE; /* clear */
card_mpuin->status |= FLAGS_READY; /* set */
card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */
card_mpuin->firstmidiq = NULL;
card_mpuin->lastmidiq = NULL;
card_mpuin->qhead = 0;
card_mpuin->qtail = 0;
sblive_miStateInit(card_mpuin);
emu10k1_mpu_reset(card);
emu10k1_mpu_acquire(card);
return 0;
}
int emu10k1_mpuin_close(struct emu10k1_card *card)
{
struct emu10k1_mpuin *card_mpuin = card->mpuin;
DPF(2, "emu10k1_mpuin_close()\n");
/* Check if there are pending input SysEx buffers */
if (card_mpuin->firstmidiq != NULL) {
ERROR();
return -1;
}
/* Disable RX interrupt */
emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDIRXENABLE : INTE_MIDIRXENABLE);
emu10k1_mpu_release(card);
card_mpuin->status |= FLAGS_AVAILABLE; /* set */
card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */
return 0;
}
/* Adds MIDI buffer to local queue list */
int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *card_mpuin, struct midi_hdr *midihdr)
{
struct midi_queue *midiq;
unsigned long flags;
DPF(2, "emu10k1_mpuin_add_buffer()\n");
/* Update MIDI buffer flags */
midihdr->flags |= MIDIBUF_INQUEUE; /* set */
midihdr->flags &= ~MIDIBUF_DONE; /* clear */
if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_ATOMIC)) == NULL) {
/* Message lost */
return -1;
}
midiq->next = NULL;
midiq->qtype = 1;
midiq->length = midihdr->bufferlength;
midiq->sizeLeft = midihdr->bufferlength;
midiq->midibyte = midihdr->data;
midiq->refdata = (unsigned long) midihdr;
spin_lock_irqsave(&card_mpuin->lock, flags);
if (card_mpuin->firstmidiq == NULL) {
card_mpuin->firstmidiq = midiq;
card_mpuin->lastmidiq = midiq;
} else {
(card_mpuin->lastmidiq)->next = midiq;
card_mpuin->lastmidiq = midiq;
}
spin_unlock_irqrestore(&card_mpuin->lock, flags);
return 0;
}
/* First set the Time Stamp if MIDI IN has not started. */
/* Then enable RX Irq. */
int emu10k1_mpuin_start(struct emu10k1_card *card)
{
struct emu10k1_mpuin *card_mpuin = card->mpuin;
u8 dummy;
DPF(2, "emu10k1_mpuin_start()\n");
/* Set timestamp if not set */
if (card_mpuin->status & FLAGS_MIDM_STARTED) {
DPF(2, "Time Stamp not changed\n");
} else {
while (!emu10k1_mpu_read_data(card, &dummy));
card_mpuin->status |= FLAGS_MIDM_STARTED; /* set */
/* Set new time stamp */
card_mpuin->timestart = (jiffies * 1000) / HZ;
DPD(2, "New Time Stamp = %d\n", card_mpuin->timestart);
card_mpuin->qhead = 0;
card_mpuin->qtail = 0;
emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDIRXENABLE : INTE_MIDIRXENABLE);
}
return 0;
}
/* Disable the RX Irq. If a partial recorded buffer */
/* exist, send it up to IMIDI level. */
int emu10k1_mpuin_stop(struct emu10k1_card *card)
{
struct emu10k1_mpuin *card_mpuin = card->mpuin;
struct midi_queue *midiq;
unsigned long flags;
DPF(2, "emu10k1_mpuin_stop()\n");
emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDIRXENABLE : INTE_MIDIRXENABLE);
card_mpuin->status &= ~FLAGS_MIDM_STARTED; /* clear */
if (card_mpuin->firstmidiq) {
spin_lock_irqsave(&card_mpuin->lock, flags);
midiq = card_mpuin->firstmidiq;
if (midiq != NULL) {
if (midiq->sizeLeft == midiq->length)
midiq = NULL;
else {
card_mpuin->firstmidiq = midiq->next;
if (card_mpuin->firstmidiq == NULL)
card_mpuin->lastmidiq = NULL;
}
}
spin_unlock_irqrestore(&card_mpuin->lock, flags);
if (midiq) {
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
kfree(midiq);
}
}
return 0;
}
/* Disable the RX Irq. If any buffer */
/* exist, send it up to IMIDI level. */
int emu10k1_mpuin_reset(struct emu10k1_card *card)
{
struct emu10k1_mpuin *card_mpuin = card->mpuin;
struct midi_queue *midiq;
DPF(2, "emu10k1_mpuin_reset()\n");
emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDIRXENABLE : INTE_MIDIRXENABLE);
while (card_mpuin->firstmidiq) {
midiq = card_mpuin->firstmidiq;
card_mpuin->firstmidiq = midiq->next;
if (midiq->sizeLeft == midiq->length)
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
else
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
kfree(midiq);
}
card_mpuin->lastmidiq = NULL;
card_mpuin->status &= ~FLAGS_MIDM_STARTED;
return 0;
}
/* Passes the message with the data back to the client */
/* via IRQ & DPC callbacks to Ring 3 */
static int emu10k1_mpuin_callback(struct emu10k1_mpuin *card_mpuin, u32 msg, unsigned long data, u32 bytesvalid)
{
unsigned long timein;
struct midi_queue *midiq;
unsigned long callback_msg[3];
struct midi_hdr *midihdr;
/* Called during ISR. The data & code touched are:
* 1. card_mpuin
* 2. The function to be called
*/
timein = card_mpuin->timein;
if (card_mpuin->timestart <= timein)
callback_msg[0] = timein - card_mpuin->timestart;
else
callback_msg[0] = (~0x0L - card_mpuin->timestart) + timein;
if (msg == ICARDMIDI_INDATA || msg == ICARDMIDI_INDATAERROR) {
callback_msg[1] = data;
callback_msg[2] = bytesvalid;
DPD(2, "emu10k1_mpuin_callback: midimsg = %#lx\n", data);
} else {
midiq = (struct midi_queue *) data;
midihdr = (struct midi_hdr *) midiq->refdata;
callback_msg[1] = midiq->length - midiq->sizeLeft;
callback_msg[2] = midiq->refdata;
midihdr->flags &= ~MIDIBUF_INQUEUE;
midihdr->flags |= MIDIBUF_DONE;
midihdr->bytesrecorded = midiq->length - midiq->sizeLeft;
}
/* Notify client that Sysex buffer has been sent */
emu10k1_midi_callback(msg, card_mpuin->openinfo.refdata, callback_msg);
return 0;
}
void emu10k1_mpuin_bh(unsigned long refdata)
{
u8 data;
unsigned idx;
struct emu10k1_mpuin *card_mpuin = (struct emu10k1_mpuin *) refdata;
unsigned long flags;
while (card_mpuin->qhead != card_mpuin->qtail) {
spin_lock_irqsave(&card_mpuin->lock, flags);
idx = card_mpuin->qhead;
data = card_mpuin->midiq[idx].data;
card_mpuin->timein = card_mpuin->midiq[idx].timein;
idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
card_mpuin->qhead = idx;
spin_unlock_irqrestore(&card_mpuin->lock, flags);
sblive_miStateEntry(card_mpuin, data);
}
return;
}
/* IRQ callback handler routine for the MPU in port */
int emu10k1_mpuin_irqhandler(struct emu10k1_card *card)
{
unsigned idx;
unsigned count;
u8 MPUIvalue;
struct emu10k1_mpuin *card_mpuin = card->mpuin;
/* IRQ service routine. The data and code touched are:
* 1. card_mpuin
*/
count = 0;
idx = card_mpuin->qtail;
while (1) {
if (emu10k1_mpu_read_data(card, &MPUIvalue) < 0) {
break;
} else {
++count;
card_mpuin->midiq[idx].data = MPUIvalue;
card_mpuin->midiq[idx].timein = (jiffies * 1000) / HZ;
idx = (idx + 1) % MIDIIN_MAX_BUFFER_SIZE;
}
}
if (count) {
card_mpuin->qtail = idx;
tasklet_hi_schedule(&card_mpuin->tasklet);
}
return 0;
}
/*****************************************************************************/
/* Supporting functions for Midi-In Interpretation State Machine */
/*****************************************************************************/
/* FIXME: This should be a macro */
static int sblive_miStateInit(struct emu10k1_mpuin *card_mpuin)
{
card_mpuin->status = 0; /* For MIDI running status */
card_mpuin->fstatus = 0; /* For 0xFn status only */
card_mpuin->curstate = STIN_PARSE;
card_mpuin->laststate = STIN_PARSE;
card_mpuin->data = 0;
card_mpuin->timestart = 0;
card_mpuin->timein = 0;
return 0;
}
/* FIXME: This should be a macro */
static int sblive_miStateEntry(struct emu10k1_mpuin *card_mpuin, u8 data)
{
return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
}
static int sblive_miStateParse(struct emu10k1_mpuin *card_mpuin, u8 data)
{
switch (data & 0xf0) {
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xE0:
card_mpuin->curstate = STIN_3BYTE;
break;
case 0xC0:
case 0xD0:
card_mpuin->curstate = STIN_2BYTE;
break;
case 0xF0:
/* System messages do not affect the previous running status! */
switch (data & 0x0f) {
case 0x0:
card_mpuin->laststate = card_mpuin->curstate;
card_mpuin->curstate = STIN_SYS_EX_NORM;
if (card_mpuin->firstmidiq) {
struct midi_queue *midiq;
midiq = card_mpuin->firstmidiq;
*midiq->midibyte = data;
--midiq->sizeLeft;
++midiq->midibyte;
}
return CTSTATUS_NEXT_BYTE;
case 0x7:
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, 0xf7, 0);
return -1;
case 0x2:
card_mpuin->laststate = card_mpuin->curstate;
card_mpuin->curstate = STIN_SYS_COMMON_3;
break;
case 0x1:
case 0x3:
card_mpuin->laststate = card_mpuin->curstate;
card_mpuin->curstate = STIN_SYS_COMMON_2;
break;
default:
/* includes 0xF4 - 0xF6, 0xF8 - 0xFF */
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
}
break;
default:
DPF(2, "BUG: default case hit\n");
return -1;
}
return midistatefn[card_mpuin->curstate].Fn(card_mpuin, data);
}
static int sblive_miState3Byte(struct emu10k1_mpuin *card_mpuin, u8 data)
{
u8 temp = data & 0xf0;
if (temp < 0x80) {
return midistatefn[STIN_3BYTE_KEY].Fn(card_mpuin, data);
} else if (temp <= 0xe0 && temp != 0xc0 && temp != 0xd0) {
card_mpuin->status = data;
card_mpuin->curstate = STIN_3BYTE_KEY;
return CTSTATUS_NEXT_BYTE;
}
return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
}
static int sblive_miState3ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = STIN_PARSE;
tmp = ((unsigned long) data) << 8;
tmp |= (unsigned long) card_mpuin->status;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->data = data;
card_mpuin->curstate = STIN_3BYTE_VEL;
return CTSTATUS_NEXT_BYTE;
}
static int sblive_miState3ByteVel(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 2 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = STIN_PARSE;
tmp = ((unsigned long) data) << 8;
tmp |= card_mpuin->data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->status;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->curstate = STIN_3BYTE;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->status;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
return 0;
}
static int sblive_miState2Byte(struct emu10k1_mpuin *card_mpuin, u8 data)
{
u8 temp = data & 0xf0;
if ((temp == 0xc0) || (temp == 0xd0)) {
card_mpuin->status = data;
card_mpuin->curstate = STIN_2BYTE_KEY;
return CTSTATUS_NEXT_BYTE;
}
if (temp < 0x80)
return midistatefn[STIN_2BYTE_KEY].Fn(card_mpuin, data);
return midistatefn[STIN_PARSE].Fn(card_mpuin, data);
}
static int sblive_miState2ByteKey(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = STIN_PARSE;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->status;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->curstate = STIN_2BYTE;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->status;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
return 0;
}
static int sblive_miStateSysCommon2(struct emu10k1_mpuin *card_mpuin, u8 data)
{
card_mpuin->fstatus = data;
card_mpuin->curstate = STIN_SYS_COMMON_2_KEY;
return CTSTATUS_NEXT_BYTE;
}
static int sblive_miStateSysCommon2Key(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = card_mpuin->laststate;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->fstatus;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->curstate = card_mpuin->laststate;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->fstatus;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 2);
return 0;
}
static int sblive_miStateSysCommon3(struct emu10k1_mpuin *card_mpuin, u8 data)
{
card_mpuin->fstatus = data;
card_mpuin->curstate = STIN_SYS_COMMON_3_KEY;
return CTSTATUS_NEXT_BYTE;
}
static int sblive_miStateSysCommon3Key(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 1 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = card_mpuin->laststate;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->fstatus;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->data = data;
card_mpuin->curstate = STIN_SYS_COMMON_3_VEL;
return CTSTATUS_NEXT_BYTE;
}
static int sblive_miStateSysCommon3Vel(struct emu10k1_mpuin *card_mpuin, u8 data)
/* byte 2 */
{
unsigned long tmp;
if (data > 0x7f) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = card_mpuin->laststate;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->fstatus;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATAERROR, tmp, 0);
return -1;
}
card_mpuin->curstate = card_mpuin->laststate;
tmp = (unsigned long) data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->data;
tmp = tmp << 8;
tmp |= (unsigned long) card_mpuin->fstatus;
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, tmp, 3);
return 0;
}
static int sblive_miStateSysExNorm(struct emu10k1_mpuin *card_mpuin, u8 data)
{
unsigned long flags;
if ((data > 0x7f) && (data != 0xf7)) {
/* Real-time messages check */
if (data > 0xf7)
return midistatefn[STIN_SYS_REAL].Fn(card_mpuin, data);
/* Invalid Data! */
DPF(2, "Invalid data!\n");
card_mpuin->curstate = card_mpuin->laststate;
if (card_mpuin->firstmidiq) {
struct midi_queue *midiq;
midiq = card_mpuin->firstmidiq;
*midiq->midibyte = data;
--midiq->sizeLeft;
++midiq->midibyte;
spin_lock_irqsave(&card_mpuin->lock, flags);
card_mpuin->firstmidiq = midiq->next;
if (card_mpuin->firstmidiq == NULL)
card_mpuin->lastmidiq = NULL;
spin_unlock_irqrestore(&card_mpuin->lock, flags);
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGERROR, (unsigned long) midiq, 0);
kfree(midiq);
}
return -1;
}
if (card_mpuin->firstmidiq) {
struct midi_queue *midiq;
midiq = card_mpuin->firstmidiq;
*midiq->midibyte = data;
--midiq->sizeLeft;
++midiq->midibyte;
}
if (data == 0xf7) {
/* End of Sysex buffer */
/* Send down the buffer */
card_mpuin->curstate = card_mpuin->laststate;
if (card_mpuin->firstmidiq) {
struct midi_queue *midiq;
midiq = card_mpuin->firstmidiq;
spin_lock_irqsave(&card_mpuin->lock, flags);
card_mpuin->firstmidiq = midiq->next;
if (card_mpuin->firstmidiq == NULL)
card_mpuin->lastmidiq = NULL;
spin_unlock_irqrestore(&card_mpuin->lock, flags);
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
kfree(midiq);
}
return 0;
}
if (card_mpuin->firstmidiq) {
struct midi_queue *midiq;
midiq = card_mpuin->firstmidiq;
if (midiq->sizeLeft == 0) {
/* Special case */
spin_lock_irqsave(&card_mpuin->lock, flags);
card_mpuin->firstmidiq = midiq->next;
if (card_mpuin->firstmidiq == NULL)
card_mpuin->lastmidiq = NULL;
spin_unlock_irqrestore(&card_mpuin->lock, flags);
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INLONGDATA, (unsigned long) midiq, 0);
kfree(midiq);
return CTSTATUS_NEXT_BYTE;
}
}
return CTSTATUS_NEXT_BYTE;
}
static int sblive_miStateSysReal(struct emu10k1_mpuin *card_mpuin, u8 data)
{
emu10k1_mpuin_callback(card_mpuin, ICARDMIDI_INDATA, data, 1);
return CTSTATUS_NEXT_BYTE;
}

View File

@@ -0,0 +1,97 @@
/*
**********************************************************************
* sblive_mi.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _CARDMI_H
#define _CARDMI_H
#include "icardmid.h"
#include <linux/interrupt.h>
typedef enum
{
STIN_PARSE = 0,
STIN_3BYTE, /* 0x80, 0x90, 0xA0, 0xB0, 0xE0 */
STIN_3BYTE_KEY, /* Byte 1 */
STIN_3BYTE_VEL, /* Byte 1 */
STIN_2BYTE, /* 0xC0, 0xD0 */
STIN_2BYTE_KEY, /* Byte 1 */
STIN_SYS_COMMON_2, /* 0xF1, 0xF3 */
STIN_SYS_COMMON_2_KEY,
STIN_SYS_COMMON_3, /* 0xF2 */
STIN_SYS_COMMON_3_KEY,
STIN_SYS_COMMON_3_VEL,
STIN_SYS_EX_NORM, /* 0xF0, Normal mode */
STIN_SYS_REAL
} midi_in_state;
/* flags for card MIDI in object */
#define FLAGS_MIDM_STARTED 0x00001000 // Data has started to come in after Midm Start
#define MIDIIN_MAX_BUFFER_SIZE 200 // Definition for struct emu10k1_mpuin
struct midi_data
{
u8 data;
u32 timein;
};
struct emu10k1_mpuin
{
spinlock_t lock;
struct midi_queue *firstmidiq;
struct midi_queue *lastmidiq;
unsigned qhead, qtail;
struct midi_data midiq[MIDIIN_MAX_BUFFER_SIZE];
struct tasklet_struct tasklet;
struct midi_openinfo openinfo;
/* For MIDI state machine */
u8 status; /* For MIDI running status */
u8 fstatus; /* For 0xFn status only */
midi_in_state curstate;
midi_in_state laststate;
u32 timestart;
u32 timein;
u8 data;
};
int emu10k1_mpuin_open(struct emu10k1_card *, struct midi_openinfo *);
int emu10k1_mpuin_close(struct emu10k1_card *);
int emu10k1_mpuin_add_buffer(struct emu10k1_mpuin *, struct midi_hdr *);
int emu10k1_mpuin_start(struct emu10k1_card *);
int emu10k1_mpuin_stop(struct emu10k1_card *);
int emu10k1_mpuin_reset(struct emu10k1_card *);
int emu10k1_mpuin_irqhandler(struct emu10k1_card *);
void emu10k1_mpuin_bh(unsigned long);
#endif /* _CARDMI_H */

229
sound/oss/emu10k1/cardmo.c Normal file
View File

@@ -0,0 +1,229 @@
/*
**********************************************************************
* cardmo.c - MIDI UART output HAL for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/slab.h>
#include "hwaccess.h"
#include "8010.h"
#include "cardmo.h"
#include "irqmgr.h"
/* Installs the IRQ handler for the MPU out port *
* and initialize parameters */
int emu10k1_mpuout_open(struct emu10k1_card *card, struct midi_openinfo *openinfo)
{
struct emu10k1_mpuout *card_mpuout = card->mpuout;
DPF(2, "emu10k1_mpuout_open()\n");
if (!(card_mpuout->status & FLAGS_AVAILABLE))
return -1;
/* Copy open info and mark channel as in use */
card_mpuout->intr = 0;
card_mpuout->openinfo = *openinfo;
card_mpuout->status &= ~FLAGS_AVAILABLE;
card_mpuout->laststatus = 0x80;
card_mpuout->firstmidiq = NULL;
card_mpuout->lastmidiq = NULL;
emu10k1_mpu_reset(card);
emu10k1_mpu_acquire(card);
return 0;
}
int emu10k1_mpuout_close(struct emu10k1_card *card)
{
struct emu10k1_mpuout *card_mpuout = card->mpuout;
struct midi_queue *midiq;
struct midi_hdr *midihdr;
unsigned long flags;
DPF(2, "emu10k1_mpuout_close()\n");
emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
spin_lock_irqsave(&card_mpuout->lock, flags);
while (card_mpuout->firstmidiq != NULL) {
midiq = card_mpuout->firstmidiq;
midihdr = (struct midi_hdr *) midiq->refdata;
card_mpuout->firstmidiq = midiq->next;
kfree(midihdr->data);
kfree(midihdr);
kfree(midiq);
}
card_mpuout->lastmidiq = NULL;
emu10k1_mpu_release(card);
card_mpuout->status |= FLAGS_AVAILABLE;
spin_unlock_irqrestore(&card_mpuout->lock, flags);
return 0;
}
/* If there isn't enough buffer space, reject Midi Buffer. *
* Otherwise, disable TX, create object to hold Midi *
* uffer, update buffer flags and other parameters *
* before enabling TX again. */
int emu10k1_mpuout_add_buffer(struct emu10k1_card *card, struct midi_hdr *midihdr)
{
struct emu10k1_mpuout *card_mpuout = card->mpuout;
struct midi_queue *midiq;
unsigned long flags;
DPF(2, "emu10k1_mpuout_add_buffer()\n");
if (card_mpuout->state == CARDMIDIOUT_STATE_SUSPEND)
return 0;
midihdr->flags |= MIDIBUF_INQUEUE;
midihdr->flags &= ~MIDIBUF_DONE;
if ((midiq = (struct midi_queue *) kmalloc(sizeof(struct midi_queue), GFP_KERNEL)) == NULL) {
/* Message lost */
return -1;
}
midiq->next = NULL;
midiq->qtype = 1;
midiq->length = midihdr->bufferlength;
midiq->sizeLeft = midihdr->bufferlength;
midiq->midibyte = midihdr->data;
midiq->refdata = (unsigned long) midihdr;
spin_lock_irqsave(&card_mpuout->lock, flags);
if (card_mpuout->firstmidiq == NULL) {
card_mpuout->firstmidiq = midiq;
card_mpuout->lastmidiq = midiq;
} else {
(card_mpuout->lastmidiq)->next = midiq;
card_mpuout->lastmidiq = midiq;
}
card_mpuout->intr = 0;
emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
spin_unlock_irqrestore(&card_mpuout->lock, flags);
return 0;
}
void emu10k1_mpuout_bh(unsigned long refdata)
{
struct emu10k1_card *card = (struct emu10k1_card *) refdata;
struct emu10k1_mpuout *card_mpuout = card->mpuout;
int cByteSent = 0;
struct midi_queue *midiq;
struct midi_queue *doneq = NULL;
unsigned long flags;
spin_lock_irqsave(&card_mpuout->lock, flags);
while (card_mpuout->firstmidiq != NULL) {
midiq = card_mpuout->firstmidiq;
while (cByteSent < 4 && midiq->sizeLeft) {
if (emu10k1_mpu_write_data(card, *midiq->midibyte) < 0) {
DPF(2, "emu10k1_mpuoutDpcCallback error!!\n");
} else {
++cByteSent;
--midiq->sizeLeft;
++midiq->midibyte;
}
}
if (midiq->sizeLeft == 0) {
if (doneq == NULL)
doneq = midiq;
card_mpuout->firstmidiq = midiq->next;
} else
break;
}
if (card_mpuout->firstmidiq == NULL)
card_mpuout->lastmidiq = NULL;
if (doneq != NULL) {
while (doneq != card_mpuout->firstmidiq) {
unsigned long callback_msg[3];
midiq = doneq;
doneq = midiq->next;
if (midiq->qtype) {
callback_msg[0] = 0;
callback_msg[1] = midiq->length;
callback_msg[2] = midiq->refdata;
emu10k1_midi_callback(ICARDMIDI_OUTLONGDATA, card_mpuout->openinfo.refdata, callback_msg);
} else if (((u8) midiq->refdata) < 0xF0 && ((u8) midiq->refdata) > 0x7F)
card_mpuout->laststatus = (u8) midiq->refdata;
kfree(midiq);
}
}
if ((card_mpuout->firstmidiq != NULL) || cByteSent) {
card_mpuout->intr = 0;
emu10k1_irq_enable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
}
spin_unlock_irqrestore(&card_mpuout->lock, flags);
return;
}
int emu10k1_mpuout_irqhandler(struct emu10k1_card *card)
{
struct emu10k1_mpuout *card_mpuout = card->mpuout;
DPF(4, "emu10k1_mpuout_irqhandler\n");
card_mpuout->intr = 1;
emu10k1_irq_disable(card, card->is_audigy ? A_INTE_MIDITXENABLE : INTE_MIDITXENABLE);
tasklet_hi_schedule(&card_mpuout->tasklet);
return 0;
}

View File

@@ -0,0 +1,62 @@
/*
**********************************************************************
* cardmo.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _CARDMO_H
#define _CARDMO_H
#include "icardmid.h"
#include <linux/interrupt.h>
#define CARDMIDIOUT_STATE_DEFAULT 0x00000000
#define CARDMIDIOUT_STATE_SUSPEND 0x00000001
struct emu10k1_mpuout
{
u32 status;
u32 state;
volatile int intr;
struct midi_queue *firstmidiq;
struct midi_queue *lastmidiq;
u8 laststatus;
struct tasklet_struct tasklet;
spinlock_t lock;
struct midi_openinfo openinfo;
};
int emu10k1_mpuout_open(struct emu10k1_card *, struct midi_openinfo *);
int emu10k1_mpuout_close(struct emu10k1_card *);
int emu10k1_mpuout_add_buffer(struct emu10k1_card *, struct midi_hdr *);
int emu10k1_mpuout_irqhandler(struct emu10k1_card *);
void emu10k1_mpuout_bh(unsigned long);
#endif /* _CARDMO_H */

373
sound/oss/emu10k1/cardwi.c Normal file
View File

@@ -0,0 +1,373 @@
/*
**********************************************************************
* cardwi.c - PCM input HAL for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/poll.h>
#include "hwaccess.h"
#include "timer.h"
#include "recmgr.h"
#include "audio.h"
#include "cardwi.h"
/**
* query_format - returns a valid sound format
*
* This function will return a valid sound format as close
* to the requested one as possible.
*/
static void query_format(int recsrc, struct wave_format *wave_fmt)
{
switch (recsrc) {
case WAVERECORD_AC97:
if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2))
wave_fmt->channels = 2;
if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2)
wave_fmt->samplingrate = 0xBB80;
else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2)
wave_fmt->samplingrate = 0xAC44;
else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2)
wave_fmt->samplingrate = 0x7D00;
else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2)
wave_fmt->samplingrate = 0x5DC0;
else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2)
wave_fmt->samplingrate = 0x5622;
else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2)
wave_fmt->samplingrate = 0x3E80;
else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2)
wave_fmt->samplingrate = 0x2B11;
else
wave_fmt->samplingrate = 0x1F40;
switch (wave_fmt->id) {
case AFMT_S16_LE:
wave_fmt->bitsperchannel = 16;
break;
case AFMT_U8:
wave_fmt->bitsperchannel = 8;
break;
default:
wave_fmt->id = AFMT_S16_LE;
wave_fmt->bitsperchannel = 16;
break;
}
break;
/* these can't be changed from the original values */
case WAVERECORD_MIC:
case WAVERECORD_FX:
break;
default:
BUG();
break;
}
wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
wave_fmt->bytespervoicesample = wave_fmt->bytespersample;
}
static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer)
{
buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov,
&buffer->dma_handle);
if (buffer->addr == NULL)
return -1;
return 0;
}
static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer)
{
if (buffer->addr != NULL)
pci_free_consistent(card->pci_dev, buffer->size * buffer->cov,
buffer->addr, buffer->dma_handle);
}
int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct wiinst *wiinst = wave_dev->wiinst;
struct wiinst **wiinst_tmp = NULL;
u16 delay;
unsigned long flags;
DPF(2, "emu10k1_wavein_open()\n");
switch (wiinst->recsrc) {
case WAVERECORD_AC97:
wiinst_tmp = &card->wavein.ac97;
break;
case WAVERECORD_MIC:
wiinst_tmp = &card->wavein.mic;
break;
case WAVERECORD_FX:
wiinst_tmp = &card->wavein.fx;
break;
default:
BUG();
break;
}
spin_lock_irqsave(&card->lock, flags);
if (*wiinst_tmp != NULL) {
spin_unlock_irqrestore(&card->lock, flags);
return -1;
}
*wiinst_tmp = wiinst;
spin_unlock_irqrestore(&card->lock, flags);
/* handle 8 bit recording */
if (wiinst->format.bytesperchannel == 1) {
if (wiinst->buffer.size > 0x8000) {
wiinst->buffer.size = 0x8000;
wiinst->buffer.sizeregval = 0x1f;
} else
wiinst->buffer.sizeregval += 4;
wiinst->buffer.cov = 2;
} else
wiinst->buffer.cov = 1;
if (alloc_buffer(card, &wiinst->buffer) < 0) {
ERROR();
return -1;
}
emu10k1_set_record_src(card, wiinst);
emu10k1_reset_record(card, &wiinst->buffer);
wiinst->buffer.hw_pos = 0;
wiinst->buffer.pos = 0;
wiinst->buffer.bytestocopy = 0;
delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec;
emu10k1_timer_install(card, &wiinst->timer, delay / 2);
wiinst->state = WAVE_STATE_OPEN;
return 0;
}
void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct wiinst *wiinst = wave_dev->wiinst;
unsigned long flags;
DPF(2, "emu10k1_wavein_close()\n");
emu10k1_wavein_stop(wave_dev);
emu10k1_timer_uninstall(card, &wiinst->timer);
free_buffer(card, &wiinst->buffer);
spin_lock_irqsave(&card->lock, flags);
switch (wave_dev->wiinst->recsrc) {
case WAVERECORD_AC97:
card->wavein.ac97 = NULL;
break;
case WAVERECORD_MIC:
card->wavein.mic = NULL;
break;
case WAVERECORD_FX:
card->wavein.fx = NULL;
break;
default:
BUG();
break;
}
spin_unlock_irqrestore(&card->lock, flags);
wiinst->state = WAVE_STATE_CLOSED;
}
void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct wiinst *wiinst = wave_dev->wiinst;
DPF(2, "emu10k1_wavein_start()\n");
emu10k1_start_record(card, &wiinst->buffer);
emu10k1_timer_enable(wave_dev->card, &wiinst->timer);
wiinst->state |= WAVE_STATE_STARTED;
}
void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct wiinst *wiinst = wave_dev->wiinst;
DPF(2, "emu10k1_wavein_stop()\n");
if (!(wiinst->state & WAVE_STATE_STARTED))
return;
emu10k1_timer_disable(card, &wiinst->timer);
emu10k1_stop_record(card, &wiinst->buffer);
wiinst->state &= ~WAVE_STATE_STARTED;
}
int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
{
struct emu10k1_card *card = wave_dev->card;
struct wiinst *wiinst = wave_dev->wiinst;
u16 delay;
DPF(2, "emu10k1_wavein_setformat()\n");
if (wiinst->state & WAVE_STATE_STARTED)
return -1;
query_format(wiinst->recsrc, format);
if ((wiinst->format.samplingrate != format->samplingrate)
|| (wiinst->format.bitsperchannel != format->bitsperchannel)
|| (wiinst->format.channels != format->channels)) {
wiinst->format = *format;
if (wiinst->state == WAVE_STATE_CLOSED)
return 0;
wiinst->buffer.size *= wiinst->buffer.cov;
if (wiinst->format.bytesperchannel == 1) {
wiinst->buffer.cov = 2;
wiinst->buffer.size /= wiinst->buffer.cov;
} else
wiinst->buffer.cov = 1;
emu10k1_timer_uninstall(card, &wiinst->timer);
delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec;
emu10k1_timer_install(card, &wiinst->timer, delay / 2);
}
return 0;
}
void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size)
{
struct wavein_buffer *buffer = &wiinst->buffer;
*size = buffer->bytestocopy;
if (wiinst->mmapped)
return;
if (*size > buffer->size) {
*size = buffer->size;
buffer->pos = buffer->hw_pos;
buffer->bytestocopy = buffer->size;
DPF(1, "buffer overrun\n");
}
}
static void copy_block(u8 __user *dst, u8 * src, u32 str, u32 len, u8 cov)
{
if (cov == 1)
__copy_to_user(dst, src + str, len);
else {
u8 byte;
u32 i;
src += 1 + 2 * str;
for (i = 0; i < len; i++) {
byte = src[2 * i] ^ 0x80;
__copy_to_user(dst + i, &byte, 1);
}
}
}
void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 __user *data, u32 * size)
{
struct wavein_buffer *buffer = &wiinst->buffer;
u32 sizetocopy, sizetocopy_now, start;
unsigned long flags;
sizetocopy = min_t(u32, buffer->size, *size);
*size = sizetocopy;
if (!sizetocopy)
return;
spin_lock_irqsave(&wiinst->lock, flags);
start = buffer->pos;
buffer->pos += sizetocopy;
buffer->pos %= buffer->size;
buffer->bytestocopy -= sizetocopy;
sizetocopy_now = buffer->size - start;
spin_unlock_irqrestore(&wiinst->lock, flags);
if (sizetocopy > sizetocopy_now) {
sizetocopy -= sizetocopy_now;
copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov);
copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov);
} else {
copy_block(data, buffer->addr, start, sizetocopy, buffer->cov);
}
}
void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst)
{
u32 hw_pos;
u32 diff;
/* There is no actual start yet */
if (!(wiinst->state & WAVE_STATE_STARTED)) {
hw_pos = wiinst->buffer.hw_pos;
} else {
/* hw_pos in byte units */
hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov;
}
diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size;
wiinst->total_recorded += diff;
wiinst->buffer.bytestocopy += diff;
wiinst->buffer.hw_pos = hw_pos;
}

View File

@@ -0,0 +1,91 @@
/*
**********************************************************************
* cardwi.h -- header file for card wave input functions
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _CARDWI_H
#define _CARDWI_H
#include "icardwav.h"
#include "audio.h"
#include "timer.h"
struct wavein_buffer {
u16 ossfragshift;
u32 fragment_size;
u32 numfrags;
u32 hw_pos; /* hardware cursor position */
u32 pos; /* software cursor position */
u32 bytestocopy; /* bytes of recorded data available */
u32 size;
u32 pages;
u32 sizereg;
u32 sizeregval;
u32 addrreg;
u32 idxreg;
u32 adcctl;
void *addr;
u8 cov;
dma_addr_t dma_handle;
};
struct wiinst
{
u8 state;
struct emu_timer timer;
struct wave_format format;
struct wavein_buffer buffer;
wait_queue_head_t wait_queue;
u8 mmapped;
u32 total_recorded; /* total bytes read() from device */
u32 blocks;
spinlock_t lock;
u8 recsrc;
u16 fxwc;
};
#define WAVEIN_MAXBUFSIZE 65536
#define WAVEIN_MINBUFSIZE 368
#define WAVEIN_DEFAULTFRAGLEN 100
#define WAVEIN_DEFAULTBUFLEN 1000
#define WAVEIN_MINFRAGSHIFT 8
#define WAVEIN_MINFRAGS 2
int emu10k1_wavein_open(struct emu10k1_wavedevice *);
void emu10k1_wavein_close(struct emu10k1_wavedevice *);
void emu10k1_wavein_start(struct emu10k1_wavedevice *);
void emu10k1_wavein_stop(struct emu10k1_wavedevice *);
void emu10k1_wavein_getxfersize(struct wiinst *, u32 *);
void emu10k1_wavein_xferdata(struct wiinst *, u8 __user *, u32 *);
int emu10k1_wavein_setformat(struct emu10k1_wavedevice *, struct wave_format *);
void emu10k1_wavein_update(struct emu10k1_card *, struct wiinst *);
#endif /* _CARDWI_H */

643
sound/oss/emu10k1/cardwo.c Normal file
View File

@@ -0,0 +1,643 @@
/*
**********************************************************************
* cardwo.c - PCM output HAL for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/poll.h>
#include "hwaccess.h"
#include "8010.h"
#include "voicemgr.h"
#include "cardwo.h"
#include "audio.h"
static u32 samplerate_to_linearpitch(u32 samplingrate)
{
samplingrate = (samplingrate << 8) / 375;
return (samplingrate >> 1) + (samplingrate & 1);
}
static void query_format(struct emu10k1_wavedevice *wave_dev, struct wave_format *wave_fmt)
{
int i, j, do_passthrough = 0, is_ac3 = 0;
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
if ((wave_fmt->channels > 2) && (wave_fmt->id != AFMT_S16_LE) && (wave_fmt->id != AFMT_U8))
wave_fmt->channels = 2;
if ((wave_fmt->channels < 1) || (wave_fmt->channels > WAVEOUT_MAXVOICES))
wave_fmt->channels = 2;
if (wave_fmt->channels == 2)
woinst->num_voices = 1;
else
woinst->num_voices = wave_fmt->channels;
if (wave_fmt->samplingrate >= 0x2ee00)
wave_fmt->samplingrate = 0x2ee00;
wave_fmt->passthrough = 0;
do_passthrough = is_ac3 = 0;
if (card->pt.selected)
do_passthrough = 1;
switch (wave_fmt->id) {
case AFMT_S16_LE:
wave_fmt->bitsperchannel = 16;
break;
case AFMT_U8:
wave_fmt->bitsperchannel = 8;
break;
case AFMT_AC3:
do_passthrough = 1;
is_ac3 = 1;
break;
default:
wave_fmt->id = AFMT_S16_LE;
wave_fmt->bitsperchannel = 16;
break;
}
if (do_passthrough) {
/* currently only one waveout instance may use pass-through */
if (woinst->state != WAVE_STATE_CLOSED ||
card->pt.state != PT_STATE_INACTIVE ||
(wave_fmt->samplingrate != 48000 && !is_ac3)) {
DPF(2, "unable to set pass-through mode\n");
} else if (USE_PT_METHOD1) {
i = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.intr_gpr_name);
j = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name, card->pt.enable_gpr_name);
if (i < 0 || j < 0)
DPF(2, "unable to set pass-through mode\n");
else {
wave_fmt->samplingrate = 48000;
wave_fmt->channels = 2;
card->pt.pos_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name,
card->pt.pos_gpr_name);
wave_fmt->passthrough = 1;
card->pt.intr_gpr = i;
card->pt.enable_gpr = j;
card->pt.state = PT_STATE_INACTIVE;
DPD(2, "is_ac3 is %d\n", is_ac3);
card->pt.ac3data = is_ac3;
wave_fmt->bitsperchannel = 16;
}
}else{
DPF(2, "Using Passthrough Method 2\n");
card->pt.enable_gpr = emu10k1_find_control_gpr(&card->mgr, card->pt.patch_name,
card->pt.enable_gpr_name);
wave_fmt->passthrough = 2;
wave_fmt->bitsperchannel = 16;
}
}
wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
if (wave_fmt->channels == 2)
wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel;
else
wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel;
}
static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum)
{
struct emu_voice *voice = &woinst->voice[voicenum];
/* Allocate voices here, if no voices available, return error. */
voice->usage = VOICE_USAGE_PLAYBACK;
voice->flags = 0;
if (woinst->format.channels == 2)
voice->flags |= VOICE_FLAGS_STEREO;
if (woinst->format.bitsperchannel == 16)
voice->flags |= VOICE_FLAGS_16BIT;
if (emu10k1_voice_alloc(card, voice) < 0) {
voice->usage = VOICE_USAGE_FREE;
return -1;
}
/* Calculate pitch */
voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch);
voice->startloop = (voice->mem.emupageindex << 12) /
woinst->format.bytespervoicesample;
voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample;
voice->start = voice->startloop;
voice->params[0].volume_target = 0xffff;
voice->params[0].initial_fc = 0xff;
voice->params[0].initial_attn = 0x00;
voice->params[0].byampl_env_sustain = 0x7f;
voice->params[0].byampl_env_decay = 0x7f;
if (voice->flags & VOICE_FLAGS_STEREO) {
if (woinst->format.passthrough == 2) {
voice->params[0].send_routing = voice->params[1].send_routing = card->waveout.send_routing[ROUTE_PT];
voice->params[0].send_routing2 = voice->params[1].send_routing2 = card->waveout.send_routing2[ROUTE_PT];
voice->params[0].send_dcba = 0xff;
voice->params[1].send_dcba = 0xff00;
voice->params[0].send_hgfe = voice->params[1].send_hgfe=0;
} else {
voice->params[0].send_dcba = card->waveout.send_dcba[SEND_LEFT];
voice->params[0].send_hgfe = card->waveout.send_hgfe[SEND_LEFT];
voice->params[1].send_dcba = card->waveout.send_dcba[SEND_RIGHT];
voice->params[1].send_hgfe = card->waveout.send_hgfe[SEND_RIGHT];
if (woinst->device) {
// /dev/dps1
voice->params[0].send_routing = voice->params[1].send_routing = card->waveout.send_routing[ROUTE_PCM1];
voice->params[0].send_routing2 = voice->params[1].send_routing2 = card->waveout.send_routing2[ROUTE_PCM1];
} else {
voice->params[0].send_routing = voice->params[1].send_routing = card->waveout.send_routing[ROUTE_PCM];
voice->params[0].send_routing2 = voice->params[1].send_routing2 = card->waveout.send_routing2[ROUTE_PCM];
}
}
voice->params[1].volume_target = 0xffff;
voice->params[1].initial_fc = 0xff;
voice->params[1].initial_attn = 0x00;
voice->params[1].byampl_env_sustain = 0x7f;
voice->params[1].byampl_env_decay = 0x7f;
} else {
if (woinst->num_voices > 1) {
// Multichannel pcm
voice->params[0].send_dcba=0xff;
voice->params[0].send_hgfe=0;
if (card->is_audigy) {
voice->params[0].send_routing = 0x3f3f3f00 + card->mchannel_fx + voicenum;
voice->params[0].send_routing2 = 0x3f3f3f3f;
} else {
voice->params[0].send_routing = 0xfff0 + card->mchannel_fx + voicenum;
}
} else {
voice->params[0].send_dcba = card->waveout.send_dcba[SEND_MONO];
voice->params[0].send_hgfe = card->waveout.send_hgfe[SEND_MONO];
if (woinst->device) {
voice->params[0].send_routing = card->waveout.send_routing[ROUTE_PCM1];
voice->params[0].send_routing2 = card->waveout.send_routing2[ROUTE_PCM1];
} else {
voice->params[0].send_routing = card->waveout.send_routing[ROUTE_PCM];
voice->params[0].send_routing2 = card->waveout.send_routing2[ROUTE_PCM];
}
}
}
DPD(2, "voice: startloop=%#x, endloop=%#x\n", voice->startloop, voice->endloop);
emu10k1_voice_playback_setup(voice);
return 0;
}
int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
struct waveout_buffer *buffer = &woinst->buffer;
unsigned int voicenum;
u16 delay;
DPF(2, "emu10k1_waveout_open()\n");
for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
if (emu10k1_voice_alloc_buffer(card, &woinst->voice[voicenum].mem, woinst->buffer.pages) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
return -1;
}
if (get_voice(card, woinst, voicenum) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
return -1;
}
}
buffer->fill_silence = 0;
buffer->silence_bytes = 0;
buffer->silence_pos = 0;
buffer->hw_pos = 0;
buffer->free_bytes = woinst->buffer.size;
delay = (48000 * woinst->buffer.fragment_size) /
(woinst->format.samplingrate * woinst->format.bytespervoicesample);
emu10k1_timer_install(card, &woinst->timer, delay);
woinst->state = WAVE_STATE_OPEN;
return 0;
}
void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
unsigned int voicenum;
DPF(2, "emu10k1_waveout_close()\n");
emu10k1_waveout_stop(wave_dev);
emu10k1_timer_uninstall(card, &woinst->timer);
for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
emu10k1_voice_free(&woinst->voice[voicenum]);
emu10k1_voice_free_buffer(card, &woinst->voice[voicenum].mem);
}
woinst->state = WAVE_STATE_CLOSED;
}
void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
struct pt_data *pt = &card->pt;
DPF(2, "emu10k1_waveout_start()\n");
if (woinst->format.passthrough == 2) {
emu10k1_pt_setup(wave_dev);
sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + pt->enable_gpr, 0, 1);
pt->state = PT_STATE_PLAYING;
}
/* Actual start */
emu10k1_voices_start(woinst->voice, woinst->num_voices, woinst->total_played);
emu10k1_timer_enable(card, &woinst->timer);
woinst->state |= WAVE_STATE_STARTED;
}
int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
unsigned int voicenum;
u16 delay;
DPF(2, "emu10k1_waveout_setformat()\n");
if (woinst->state & WAVE_STATE_STARTED)
return -1;
query_format(wave_dev, format);
if (woinst->format.samplingrate != format->samplingrate ||
woinst->format.channels != format->channels ||
woinst->format.bitsperchannel != format->bitsperchannel) {
woinst->format = *format;
if (woinst->state == WAVE_STATE_CLOSED)
return 0;
emu10k1_timer_uninstall(card, &woinst->timer);
for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
emu10k1_voice_free(&woinst->voice[voicenum]);
if (get_voice(card, woinst, voicenum) < 0) {
ERROR();
emu10k1_waveout_close(wave_dev);
return -1;
}
}
delay = (48000 * woinst->buffer.fragment_size) /
(woinst->format.samplingrate * woinst->format.bytespervoicesample);
emu10k1_timer_install(card, &woinst->timer, delay);
}
return 0;
}
void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev)
{
struct emu10k1_card *card = wave_dev->card;
struct woinst *woinst = wave_dev->woinst;
DPF(2, "emu10k1_waveout_stop()\n");
if (!(woinst->state & WAVE_STATE_STARTED))
return;
emu10k1_timer_disable(card, &woinst->timer);
/* Stop actual voices */
emu10k1_voices_stop(woinst->voice, woinst->num_voices);
emu10k1_waveout_update(woinst);
woinst->state &= ~WAVE_STATE_STARTED;
}
/**
* emu10k1_waveout_getxfersize -
*
* gives the total free bytes on the voice buffer, including silence bytes
* (basically: total_free_bytes = free_bytes + silence_bytes).
*
*/
void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 *total_free_bytes)
{
struct waveout_buffer *buffer = &woinst->buffer;
int pending_bytes;
if (woinst->mmapped) {
*total_free_bytes = buffer->free_bytes;
return;
}
pending_bytes = buffer->size - buffer->free_bytes;
buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size * 2) ? 1 : 0;
if (pending_bytes > (signed) buffer->silence_bytes) {
*total_free_bytes = (buffer->free_bytes + buffer->silence_bytes);
} else {
*total_free_bytes = buffer->size;
buffer->silence_bytes = pending_bytes;
if (pending_bytes < 0) {
buffer->silence_pos = buffer->hw_pos;
buffer->silence_bytes = 0;
buffer->free_bytes = buffer->size;
DPF(1, "buffer underrun\n");
}
}
}
/**
* copy_block -
*
* copies a block of pcm data to a voice buffer.
* Notice that the voice buffer is actually a set of disjointed memory pages.
*
*/
static void copy_block(void **dst, u32 str, u8 __user *src, u32 len)
{
unsigned int pg;
unsigned int pgoff;
unsigned int k;
pg = str / PAGE_SIZE;
pgoff = str % PAGE_SIZE;
if (len > PAGE_SIZE - pgoff) {
k = PAGE_SIZE - pgoff;
if (__copy_from_user((u8 *)dst[pg] + pgoff, src, k))
return;
len -= k;
while (len > PAGE_SIZE) {
if (__copy_from_user(dst[++pg], src + k, PAGE_SIZE))
return;
k += PAGE_SIZE;
len -= PAGE_SIZE;
}
if (__copy_from_user(dst[++pg], src + k, len))
return;
} else
__copy_from_user((u8 *)dst[pg] + pgoff, src, len);
}
/**
* copy_ilv_block -
*
* copies a block of pcm data containing n interleaved channels to n mono voice buffers.
* Notice that the voice buffer is actually a set of disjointed memory pages.
*
*/
static void copy_ilv_block(struct woinst *woinst, u32 str, u8 __user *src, u32 len)
{
unsigned int pg;
unsigned int pgoff;
unsigned int voice_num;
struct emu_voice *voice = woinst->voice;
pg = str / PAGE_SIZE;
pgoff = str % PAGE_SIZE;
while (len) {
for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) {
if (__copy_from_user((u8 *)(voice[voice_num].mem.addr[pg]) + pgoff, src, woinst->format.bytespervoicesample))
return;
src += woinst->format.bytespervoicesample;
}
len -= woinst->format.bytespervoicesample;
pgoff += woinst->format.bytespervoicesample;
if (pgoff >= PAGE_SIZE) {
pgoff = 0;
pg++;
}
}
}
/**
* fill_block -
*
* fills a set voice buffers with a block of a given sample.
*
*/
static void fill_block(struct woinst *woinst, u32 str, u8 data, u32 len)
{
unsigned int pg;
unsigned int pgoff;
unsigned int voice_num;
struct emu_voice *voice = woinst->voice;
unsigned int k;
pg = str / PAGE_SIZE;
pgoff = str % PAGE_SIZE;
if (len > PAGE_SIZE - pgoff) {
k = PAGE_SIZE - pgoff;
for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
memset((u8 *)voice[voice_num].mem.addr[pg] + pgoff, data, k);
len -= k;
while (len > PAGE_SIZE) {
pg++;
for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
memset(voice[voice_num].mem.addr[pg], data, PAGE_SIZE);
len -= PAGE_SIZE;
}
pg++;
for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
memset(voice[voice_num].mem.addr[pg], data, len);
} else {
for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
memset((u8 *)voice[voice_num].mem.addr[pg] + pgoff, data, len);
}
}
/**
* emu10k1_waveout_xferdata -
*
* copies pcm data to the voice buffer. Silence samples
* previously added to the buffer are overwritten.
*
*/
void emu10k1_waveout_xferdata(struct woinst *woinst, u8 __user *data, u32 *size)
{
struct waveout_buffer *buffer = &woinst->buffer;
struct voice_mem *mem = &woinst->voice[0].mem;
u32 sizetocopy, sizetocopy_now, start;
unsigned long flags;
sizetocopy = min_t(u32, buffer->size, *size);
*size = sizetocopy;
if (!sizetocopy)
return;
spin_lock_irqsave(&woinst->lock, flags);
start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size;
if (sizetocopy > buffer->silence_bytes) {
buffer->silence_pos += sizetocopy - buffer->silence_bytes;
buffer->free_bytes -= sizetocopy - buffer->silence_bytes;
buffer->silence_bytes = 0;
} else
buffer->silence_bytes -= sizetocopy;
spin_unlock_irqrestore(&woinst->lock, flags);
sizetocopy_now = buffer->size - start;
if (sizetocopy > sizetocopy_now) {
sizetocopy -= sizetocopy_now;
if (woinst->num_voices > 1) {
copy_ilv_block(woinst, start, data, sizetocopy_now);
copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy);
} else {
copy_block(mem->addr, start, data, sizetocopy_now);
copy_block(mem->addr, 0, data + sizetocopy_now, sizetocopy);
}
} else {
if (woinst->num_voices > 1)
copy_ilv_block(woinst, start, data, sizetocopy);
else
copy_block(mem->addr, start, data, sizetocopy);
}
}
/**
* emu10k1_waveout_fillsilence -
*
* adds samples of silence to the voice buffer so that we
* don't loop over stale pcm data.
*
*/
void emu10k1_waveout_fillsilence(struct woinst *woinst)
{
struct waveout_buffer *buffer = &woinst->buffer;
u32 sizetocopy, sizetocopy_now, start;
u8 filldata;
unsigned long flags;
sizetocopy = buffer->fragment_size;
if (woinst->format.bitsperchannel == 16)
filldata = 0x00;
else
filldata = 0x80;
spin_lock_irqsave(&woinst->lock, flags);
buffer->silence_bytes += sizetocopy;
buffer->free_bytes -= sizetocopy;
buffer->silence_pos %= buffer->size;
start = buffer->silence_pos;
buffer->silence_pos += sizetocopy;
spin_unlock_irqrestore(&woinst->lock, flags);
sizetocopy_now = buffer->size - start;
if (sizetocopy > sizetocopy_now) {
sizetocopy -= sizetocopy_now;
fill_block(woinst, start, filldata, sizetocopy_now);
fill_block(woinst, 0, filldata, sizetocopy);
} else {
fill_block(woinst, start, filldata, sizetocopy);
}
}
/**
* emu10k1_waveout_update -
*
* updates the position of the voice buffer hardware pointer (hw_pos)
* and the number of free bytes on the buffer (free_bytes).
* The free bytes _don't_ include silence bytes that may have been
* added to the buffer.
*
*/
void emu10k1_waveout_update(struct woinst *woinst)
{
u32 hw_pos;
u32 diff;
/* There is no actual start yet */
if (!(woinst->state & WAVE_STATE_STARTED)) {
hw_pos = woinst->buffer.hw_pos;
} else {
/* hw_pos in sample units */
hw_pos = sblive_readptr(woinst->voice[0].card, CCCA_CURRADDR, woinst->voice[0].num);
if(hw_pos < woinst->voice[0].start)
hw_pos += woinst->buffer.size / woinst->format.bytespervoicesample - woinst->voice[0].start;
else
hw_pos -= woinst->voice[0].start;
hw_pos *= woinst->format.bytespervoicesample;
}
diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size;
woinst->total_played += diff;
woinst->buffer.free_bytes += diff;
woinst->buffer.hw_pos = hw_pos;
}

View File

@@ -0,0 +1,90 @@
/*
**********************************************************************
* cardwo.h -- header file for card wave out functions
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _CARDWO_H
#define _CARDWO_H
#include "icardwav.h"
#include "audio.h"
#include "voicemgr.h"
#include "timer.h"
/* setting this to other than a power of two may break some applications */
#define WAVEOUT_MAXBUFSIZE MAXBUFSIZE
#define WAVEOUT_DEFAULTFRAGLEN 20 /* Time to play a fragment in ms (latency) */
#define WAVEOUT_DEFAULTBUFLEN 500 /* Time to play the entire buffer in ms */
#define WAVEOUT_MINFRAGSHIFT 6 /* Minimum fragment size in bytes is 2^6 */
#define WAVEOUT_MINFRAGS 3 /* _don't_ go bellow 3, it would break silence filling */
#define WAVEOUT_MAXVOICES 6
struct waveout_buffer {
u16 ossfragshift;
u32 numfrags;
u32 fragment_size; /* in bytes units */
u32 size; /* in bytes units */
u32 pages; /* buffer size in page units*/
u32 silence_pos; /* software cursor position (including silence bytes) */
u32 hw_pos; /* hardware cursor position */
u32 free_bytes; /* free bytes available on the buffer (not including silence bytes) */
u8 fill_silence;
u32 silence_bytes; /* silence bytes on the buffer */
};
struct woinst
{
u8 state;
u8 num_voices;
struct emu_voice voice[WAVEOUT_MAXVOICES];
struct emu_timer timer;
struct wave_format format;
struct waveout_buffer buffer;
wait_queue_head_t wait_queue;
u8 mmapped;
u32 total_copied; /* total number of bytes written() to the buffer (excluding silence) */
u32 total_played; /* total number of bytes played including silence */
u32 blocks;
u8 device;
spinlock_t lock;
};
int emu10k1_waveout_open(struct emu10k1_wavedevice *);
void emu10k1_waveout_close(struct emu10k1_wavedevice *);
void emu10k1_waveout_start(struct emu10k1_wavedevice *);
void emu10k1_waveout_stop(struct emu10k1_wavedevice *);
void emu10k1_waveout_getxfersize(struct woinst*, u32 *);
void emu10k1_waveout_xferdata(struct woinst*, u8 __user *, u32 *);
void emu10k1_waveout_fillsilence(struct woinst*);
int emu10k1_waveout_setformat(struct emu10k1_wavedevice*, struct wave_format*);
void emu10k1_waveout_update(struct woinst*);
#endif /* _CARDWO_H */

157
sound/oss/emu10k1/ecard.c Normal file
View File

@@ -0,0 +1,157 @@
/*
**********************************************************************
* ecard.c - E-card initialization code
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include "ecard.h"
#include "hwaccess.h"
/* Private routines */
static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16);
static void ecard_write(struct emu10k1_card *, u32);
/**************************************************************************
* @func Set the gain of the ECARD's CS3310 Trim/gain controller. The
* trim value consists of a 16bit value which is composed of two
* 8 bit gain/trim values, one for the left channel and one for the
* right channel. The following table maps from the Gain/Attenuation
* value in decibels into the corresponding bit pattern for a single
* channel.
*/
static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain)
{
u32 currbit;
ecard->adc_gain = gain;
/* Enable writing to the TRIM registers */
ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
/* Do it again to insure that we meet hold time requirements */
ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN);
for (currbit = (1L << 15); currbit; currbit >>= 1) {
u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA);
if (gain & currbit)
value |= EC_TRIM_SDATA;
/* Clock the bit */
ecard_write(card, value);
ecard_write(card, value | EC_TRIM_SCLK);
ecard_write(card, value);
}
ecard_write(card, ecard->control_bits);
}
/**************************************************************************
* @func Clock bits into the Ecard's control latch. The Ecard uses a
* control latch will is loaded bit-serially by toggling the Modem control
* lines from function 2 on the E8010. This function hides these details
* and presents the illusion that we are actually writing to a distinct
* register.
*/
static void ecard_write(struct emu10k1_card *card, u32 value)
{
u16 count;
u32 data, hcvalue;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT);
outl(card->iobase + HCFG, hcvalue);
for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) {
/* Set up the value */
data = ((value & 0x1) ? PULSEN_BIT : 0);
value >>= 1;
outl(card->iobase + HCFG, hcvalue | data);
/* Clock the shift register */
outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT);
outl(card->iobase + HCFG, hcvalue | data);
}
/* Latch the bits */
outl(card->iobase + HCFG, hcvalue | HOOKN_BIT);
outl(card->iobase + HCFG, hcvalue);
spin_unlock_irqrestore(&card->lock, flags);
}
void __devinit emu10k1_ecard_init(struct emu10k1_card *card)
{
u32 hcvalue;
struct ecard_state ecard;
/* Set up the initial settings */
ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL;
ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL;
ecard.mux2_setting = 0;
ecard.adc_gain = EC_DEFAULT_ADC_GAIN;
ecard.control_bits = EC_RAW_RUN_MODE |
EC_SPDIF0_SELECT(ecard.mux0_setting) |
EC_SPDIF1_SELECT(ecard.mux1_setting);
/* Step 0: Set the codec type in the hardware control register
* and enable audio output */
hcvalue = emu10k1_readfn0(card, HCFG);
emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S);
/* Step 1: Turn off the led and deassert TRIM_CS */
ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
/* Step 2: Calibrate the ADC and DAC */
ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
/* Step 3: Wait for awhile; FIXME: Is this correct? */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ);
/* Step 4: Switch off the DAC and ADC calibration. Note
* That ADC_CAL is actually an inverted signal, so we assert
* it here to stop calibration. */
ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
/* Step 4: Switch into run mode */
ecard_write(card, ecard.control_bits);
/* Step 5: Set the analog input gain */
ecard_setadcgain(card, &ecard, ecard.adc_gain);
}

113
sound/oss/emu10k1/ecard.h Normal file
View File

@@ -0,0 +1,113 @@
/*
**********************************************************************
* ecard.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _ECARD_H
#define _ECARD_H
#include "8010.h"
#include "hwaccess.h"
#include <linux/init.h>
/* In A1 Silicon, these bits are in the HC register */
#define HOOKN_BIT (1L << 12)
#define HANDN_BIT (1L << 11)
#define PULSEN_BIT (1L << 10)
#define EC_GDI1 (1 << 13)
#define EC_GDI0 (1 << 14)
#define EC_NUM_CONTROL_BITS 20
#define EC_AC3_DATA_SELN 0x0001L
#define EC_EE_DATA_SEL 0x0002L
#define EC_EE_CNTRL_SELN 0x0004L
#define EC_EECLK 0x0008L
#define EC_EECS 0x0010L
#define EC_EESDO 0x0020L
#define EC_TRIM_CSN 0x0040L
#define EC_TRIM_SCLK 0x0080L
#define EC_TRIM_SDATA 0x0100L
#define EC_TRIM_MUTEN 0x0200L
#define EC_ADCCAL 0x0400L
#define EC_ADCRSTN 0x0800L
#define EC_DACCAL 0x1000L
#define EC_DACMUTEN 0x2000L
#define EC_LEDN 0x4000L
#define EC_SPDIF0_SEL_SHIFT 15
#define EC_SPDIF1_SEL_SHIFT 17
#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT)
#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT)
#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should
* be incremented any time the EEPROM's
* format is changed. */
#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */
/* Addresses for special values stored in to EEPROM */
#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */
#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */
#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */
#define EC_LAST_PROMFILE_ADDR 0x2f
#define EC_SERIALNUM_ADD 0x30 /* First word of serial number. The number
* can be up to 30 characters in length
* and is stored as a NULL-terminated
* ASCII string. Any unused bytes must be
* filled with zeros */
#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */
/* Most of this stuff is pretty self-evident. According to the hardware
* dudes, we need to leave the ADCCAL bit low in order to avoid a DC
* offset problem. Weird.
*/
#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | EC_TRIM_CSN)
#define EC_DEFAULT_ADC_GAIN 0xC4C4
#define EC_DEFAULT_SPDIF0_SEL 0x0
#define EC_DEFAULT_SPDIF1_SEL 0x4
#define HC_EA 0x01L
/* ECARD state structure. This structure maintains the state
* for various portions of the ECARD's onboard hardware.
*/
struct ecard_state {
u32 control_bits;
u16 adc_gain;
u16 mux0_setting;
u16 mux1_setting;
u16 mux2_setting;
};
void emu10k1_ecard_init(struct emu10k1_card *) __devinit;
#endif /* _ECARD_H */

220
sound/oss/emu10k1/efxmgr.c Normal file
View File

@@ -0,0 +1,220 @@
/*
**********************************************************************
* efxmgr.c
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/bitops.h>
#include "hwaccess.h"
#include "efxmgr.h"
int emu10k1_find_control_gpr(struct patch_manager *mgr, const char *patch_name, const char *gpr_name)
{
struct dsp_patch *patch;
struct dsp_rpatch *rpatch;
char s[PATCH_NAME_SIZE + 4];
unsigned long *gpr_used;
int i;
DPD(2, "emu10k1_find_control_gpr(): %s %s\n", patch_name, gpr_name);
rpatch = &mgr->rpatch;
if (!strcmp(rpatch->name, patch_name)) {
gpr_used = rpatch->gpr_used;
goto match;
}
for (i = 0; i < mgr->current_pages * PATCHES_PER_PAGE; i++) {
patch = PATCH(mgr, i);
sprintf(s,"%s", patch->name);
if (!strcmp(s, patch_name)) {
gpr_used = patch->gpr_used;
goto match;
}
}
return -1;
match:
for (i = 0; i < NUM_GPRS; i++)
if (mgr->gpr[i].type == GPR_TYPE_CONTROL &&
test_bit(i, gpr_used) &&
!strcmp(mgr->gpr[i].name, gpr_name))
return i;
return -1;
}
void emu10k1_set_control_gpr(struct emu10k1_card *card, int addr, s32 val, int flag)
{
struct patch_manager *mgr = &card->mgr;
DPD(2, "emu10k1_set_control_gpr(): %d %x\n", addr, val);
if (addr < 0 || addr >= NUM_GPRS)
return;
//fixme: once patch manager is up, remember to fix this for the audigy
if (card->is_audigy) {
sblive_writeptr(card, A_GPR_BASE + addr, 0, val);
} else {
if (flag)
val += sblive_readptr(card, GPR_BASE + addr, 0);
if (val > mgr->gpr[addr].max)
val = mgr->gpr[addr].max;
else if (val < mgr->gpr[addr].min)
val = mgr->gpr[addr].min;
sblive_writeptr(card, GPR_BASE + addr, 0, val);
}
}
//TODO: make this configurable:
#define VOLCTRL_CHANNEL SOUND_MIXER_VOLUME
#define VOLCTRL_STEP_SIZE 5
//An internal function for setting OSS mixer controls.
static void emu10k1_set_oss_vol(struct emu10k1_card *card, int oss_mixer,
unsigned int left, unsigned int right)
{
extern char volume_params[SOUND_MIXER_NRDEVICES];
card->ac97->mixer_state[oss_mixer] = (right << 8) | left;
if (!card->is_aps)
card->ac97->write_mixer(card->ac97, oss_mixer, left, right);
emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left,
volume_params[oss_mixer]);
emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right,
volume_params[oss_mixer]);
}
//FIXME: mute should unmute when pressed a second time
void emu10k1_mute_irqhandler(struct emu10k1_card *card)
{
int oss_channel = VOLCTRL_CHANNEL;
int left, right;
static int val;
if (val) {
left = val & 0xff;
right = (val >> 8) & 0xff;
val = 0;
} else {
val = card->ac97->mixer_state[oss_channel];
left = 0;
right = 0;
}
emu10k1_set_oss_vol(card, oss_channel, left, right);
}
void emu10k1_volincr_irqhandler(struct emu10k1_card *card)
{
int oss_channel = VOLCTRL_CHANNEL;
int left, right;
left = card->ac97->mixer_state[oss_channel] & 0xff;
right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff;
if ((left += VOLCTRL_STEP_SIZE) > 100)
left = 100;
if ((right += VOLCTRL_STEP_SIZE) > 100)
right = 100;
emu10k1_set_oss_vol(card, oss_channel, left, right);
}
void emu10k1_voldecr_irqhandler(struct emu10k1_card *card)
{
int oss_channel = VOLCTRL_CHANNEL;
int left, right;
left = card->ac97->mixer_state[oss_channel] & 0xff;
right = (card->ac97->mixer_state[oss_channel] >> 8) & 0xff;
if ((left -= VOLCTRL_STEP_SIZE) < 0)
left = 0;
if ((right -= VOLCTRL_STEP_SIZE) < 0)
right = 0;
emu10k1_set_oss_vol(card, oss_channel, left, right);
}
void emu10k1_set_volume_gpr(struct emu10k1_card *card, int addr, s32 vol, int scale)
{
struct patch_manager *mgr = &card->mgr;
unsigned long flags;
static const s32 log2lin[4] ={ // attenuation (dB)
0x7fffffff, // 0.0
0x7fffffff * 0.840896415253715 , // 1.5
0x7fffffff * 0.707106781186548, // 3.0
0x7fffffff * 0.594603557501361 , // 4.5
};
if (addr < 0)
return;
vol = (100 - vol ) * scale / 100;
// Thanks to the comp.dsp newsgroup for this neat trick:
vol = (vol >= scale) ? 0 : (log2lin[vol & 3] >> (vol >> 2));
spin_lock_irqsave(&mgr->lock, flags);
emu10k1_set_control_gpr(card, addr, vol, 0);
spin_unlock_irqrestore(&mgr->lock, flags);
}
void emu10k1_dsp_irqhandler(struct emu10k1_card *card)
{
unsigned long flags;
if (card->pt.state != PT_STATE_INACTIVE) {
u32 bc;
bc = sblive_readptr(card, GPR_BASE + card->pt.intr_gpr, 0);
if (bc != 0) {
DPD(3, "pt interrupt, bc = %d\n", bc);
spin_lock_irqsave(&card->pt.lock, flags);
card->pt.blocks_played = bc;
if (card->pt.blocks_played >= card->pt.blocks_copied) {
DPF(1, "buffer underrun in passthrough playback\n");
emu10k1_pt_stop(card);
}
wake_up_interruptible(&card->pt.wait);
spin_unlock_irqrestore(&card->pt.lock, flags);
}
}
}

270
sound/oss/emu10k1/efxmgr.h Normal file
View File

@@ -0,0 +1,270 @@
/*
**********************************************************************
* sblive_fx.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _EFXMGR_H
#define _EFXMGR_H
struct emu_efx_info_t{
int opcode_shift;
int high_operand_shift;
int instruction_start;
int gpr_base;
int output_base;
};
#define WRITE_EFX(a, b, c) sblive_writeptr((a), emu_efx_info[card->is_audigy].instruction_start + (b), 0, (c))
#define OP(op, z, w, x, y) \
do { WRITE_EFX(card, (pc) * 2, ((x) << emu_efx_info[card->is_audigy].high_operand_shift) | (y)); \
WRITE_EFX(card, (pc) * 2 + 1, ((op) << emu_efx_info[card->is_audigy].opcode_shift ) | ((z) << emu_efx_info[card->is_audigy].high_operand_shift) | (w)); \
++pc; } while (0)
#define NUM_INPUTS 0x20
#define NUM_OUTPUTS 0x20
#define NUM_GPRS 0x100
#define A_NUM_INPUTS 0x60
#define A_NUM_OUTPUTS 0x60 //fixme: this may or may not be true
#define A_NUM_GPRS 0x200
#define GPR_NAME_SIZE 32
#define PATCH_NAME_SIZE 32
struct dsp_rpatch {
char name[PATCH_NAME_SIZE];
u16 code_start;
u16 code_size;
unsigned long gpr_used[NUM_GPRS / (sizeof(unsigned long) * 8) + 1];
unsigned long gpr_input[NUM_GPRS / (sizeof(unsigned long) * 8) + 1];
unsigned long route[NUM_OUTPUTS];
unsigned long route_v[NUM_OUTPUTS];
};
struct dsp_patch {
char name[PATCH_NAME_SIZE];
u8 id;
unsigned long input; /* bitmap of the lines used as inputs */
unsigned long output; /* bitmap of the lines used as outputs */
u16 code_start;
u16 code_size;
unsigned long gpr_used[NUM_GPRS / (sizeof(unsigned long) * 8) + 1]; /* bitmap of used gprs */
unsigned long gpr_input[NUM_GPRS / (sizeof(unsigned long) * 8) + 1];
u8 traml_istart; /* starting address of the internal tram lines used */
u8 traml_isize; /* number of internal tram lines used */
u8 traml_estart;
u8 traml_esize;
u16 tramb_istart; /* starting address of the internal tram memory used */
u16 tramb_isize; /* amount of internal memory used */
u32 tramb_estart;
u32 tramb_esize;
};
struct dsp_gpr {
u8 type; /* gpr type, STATIC, DYNAMIC, INPUT, OUTPUT, CONTROL */
char name[GPR_NAME_SIZE]; /* gpr value, only valid for control gprs */
s32 min, max; /* value range for this gpr, only valid for control gprs */
u8 line; /* which input/output line is the gpr attached, only valid for input/output gprs */
u8 usage;
};
enum {
GPR_TYPE_NULL = 0,
GPR_TYPE_IO,
GPR_TYPE_STATIC,
GPR_TYPE_DYNAMIC,
GPR_TYPE_CONTROL,
GPR_TYPE_CONSTANT
};
#define GPR_BASE 0x100
#define OUTPUT_BASE 0x20
#define A_GPR_BASE 0x400
#define A_OUTPUT_BASE 0x60
#define MAX_PATCHES_PAGES 32
struct patch_manager {
void *patch[MAX_PATCHES_PAGES];
int current_pages;
struct dsp_rpatch rpatch;
struct dsp_gpr gpr[NUM_GPRS]; /* gpr usage table */
spinlock_t lock;
s16 ctrl_gpr[SOUND_MIXER_NRDEVICES][2];
};
#define PATCHES_PER_PAGE (PAGE_SIZE / sizeof(struct dsp_patch))
#define PATCH(mgr, i) ((struct dsp_patch *) (mgr)->patch[(i) / PATCHES_PER_PAGE] + (i) % PATCHES_PER_PAGE)
/* PCM volume control */
#define TMP_PCM_L 0x100 //temp PCM L (after the vol control)
#define TMP_PCM_R 0x101
#define VOL_PCM_L 0x102 //vol PCM
#define VOL_PCM_R 0x103
/* Routing patch */
#define TMP_AC_L 0x104 //tmp ac97 out
#define TMP_AC_R 0x105
#define TMP_REAR_L 0x106 //output - Temp Rear
#define TMP_REAR_R 0x107
#define TMP_DIGI_L 0x108 //output - Temp digital
#define TMP_DIGI_R 0x109
#define DSP_VOL_L 0x10a // main dsp volume
#define DSP_VOL_R 0x10b
/* hw inputs */
#define PCM_IN_L 0x00
#define PCM_IN_R 0x01
#define PCM1_IN_L 0x04
#define PCM1_IN_R 0x05
//mutilchannel playback stream appear here:
#define MULTI_FRONT_L 0x08
#define MULTI_FRONT_R 0x09
#define MULTI_REAR_L 0x0a
#define MULTI_REAR_R 0x0b
#define MULTI_CENTER 0x0c
#define MULTI_LFE 0x0d
#define AC97_IN_L 0x10
#define AC97_IN_R 0x11
#define SPDIF_CD_L 0x12
#define SPDIF_CD_R 0x13
/* hw outputs */
#define AC97_FRONT_L 0x20
#define AC97_FRONT_R 0x21
#define DIGITAL_OUT_L 0x22
#define DIGITAL_OUT_R 0x23
#define DIGITAL_CENTER 0x24
#define DIGITAL_LFE 0x25
#define ANALOG_REAR_L 0x28
#define ANALOG_REAR_R 0x29
#define ADC_REC_L 0x2a
#define ADC_REC_R 0x2b
#define ANALOG_CENTER 0x31
#define ANALOG_LFE 0x32
#define INPUT_PATCH_START(patch, nm, ln, i) \
do { \
patch = PATCH(mgr, patch_n); \
strcpy(patch->name, nm); \
patch->code_start = pc * 2; \
patch->input = (1<<(0x1f&ln)); \
patch->output= (1<<(0x1f&ln)); \
patch->id = i; \
} while(0)
#define INPUT_PATCH_END(patch) \
do { \
patch->code_size = pc * 2 - patch->code_start; \
patch_n++; \
} while(0)
#define ROUTING_PATCH_START(patch, nm) \
do { \
patch = &mgr->rpatch; \
strcpy(patch->name, nm); \
patch->code_start = pc * 2; \
} while(0)
#define ROUTING_PATCH_END(patch) \
do { \
patch->code_size = pc * 2 - patch->code_start; \
} while(0)
#define CONNECT(input, output) set_bit(input, &rpatch->route[(output) - OUTPUT_BASE]);
#define CONNECT_V(input, output) set_bit(input, &rpatch->route_v[(output) - OUTPUT_BASE]);
#define OUTPUT_PATCH_START(patch, nm, ln, i) \
do { \
patch = PATCH(mgr, patch_n); \
strcpy(patch->name, nm); \
patch->code_start = pc * 2; \
patch->input = (1<<(0x1f&ln)); \
patch->output= (1<<(0x1f&ln)); \
patch->id = i; \
} while(0)
#define OUTPUT_PATCH_END(patch) \
do { \
patch->code_size = pc * 2 - patch->code_start; \
patch_n++; \
} while(0)
#define GET_OUTPUT_GPR(patch, g, ln) \
do { \
mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \
mgr->gpr[(g) - GPR_BASE].usage++; \
mgr->gpr[(g) - GPR_BASE].line = ln; \
set_bit((g) - GPR_BASE, patch->gpr_used); \
} while(0)
#define GET_INPUT_GPR(patch, g, ln) \
do { \
mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_IO; \
mgr->gpr[(g) - GPR_BASE].usage++; \
mgr->gpr[(g) - GPR_BASE].line = ln; \
set_bit((g) - GPR_BASE, patch->gpr_used); \
set_bit((g) - GPR_BASE, patch->gpr_input); \
} while(0)
#define GET_DYNAMIC_GPR(patch, g) \
do { \
mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_DYNAMIC; \
mgr->gpr[(g) - GPR_BASE].usage++; \
set_bit((g) - GPR_BASE, patch->gpr_used); \
} while(0)
#define GET_CONTROL_GPR(patch, g, nm, a, b) \
do { \
strcpy(mgr->gpr[(g) - GPR_BASE].name, nm); \
mgr->gpr[(g) - GPR_BASE].type = GPR_TYPE_CONTROL; \
mgr->gpr[(g) - GPR_BASE].usage++; \
mgr->gpr[(g) - GPR_BASE].min = a; \
mgr->gpr[(g) - GPR_BASE].max = b; \
sblive_writeptr(card, g, 0, b); \
set_bit((g) - GPR_BASE, patch->gpr_used); \
} while(0)
#endif /* _EFXMGR_H */

View File

@@ -0,0 +1,104 @@
/*
**********************************************************************
* emuadxmg.c - Address space manager for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include "hwaccess.h"
/* Allocates emu address space */
int emu10k1_addxmgr_alloc(u32 size, struct emu10k1_card *card)
{
u16 *pagetable = card->emupagetable;
u16 index = 0;
u16 numpages;
unsigned long flags;
/* Convert bytes to pages */
numpages = (size / EMUPAGESIZE) + ((size % EMUPAGESIZE) ? 1 : 0);
spin_lock_irqsave(&card->lock, flags);
while (index < (MAXPAGES - 1)) {
if (pagetable[index] & 0x8000) {
/* This block of pages is in use, jump to the start of the next block. */
index += (pagetable[index] & 0x7fff);
} else {
/* Found free block */
if (pagetable[index] >= numpages) {
/* Block is large enough */
/* If free block is larger than the block requested
* then adjust the size of the block remaining */
if (pagetable[index] > numpages)
pagetable[index + numpages] = pagetable[index] - numpages;
pagetable[index] = (numpages | 0x8000); /* Mark block as used */
spin_unlock_irqrestore(&card->lock, flags);
return index;
} else {
/* Block too small, jump to the start of the next block */
index += pagetable[index];
}
}
}
spin_unlock_irqrestore(&card->lock, flags);
return -1;
}
/* Frees a previously allocated emu address space. */
void emu10k1_addxmgr_free(struct emu10k1_card *card, int index)
{
u16 *pagetable = card->emupagetable;
u16 origsize = 0;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
if (pagetable[index] & 0x8000) {
/* Block is allocated - mark block as free */
origsize = pagetable[index] & 0x7fff;
pagetable[index] = origsize;
/* If next block is free, we concat both blocks */
if (!(pagetable[index + origsize] & 0x8000))
pagetable[index] += pagetable[index + origsize] & 0x7fff;
}
spin_unlock_irqrestore(&card->lock, flags);
return;
}

View File

@@ -0,0 +1,507 @@
/*
**********************************************************************
* hwaccess.c -- Hardware access layer
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* December 9, 1999 Jon Taylor rewrote the I/O subsystem
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <asm/io.h>
#include "hwaccess.h"
#include "8010.h"
#include "icardmid.h"
/*************************************************************************
* Function : srToPitch *
* Input : sampleRate - sampling rate *
* Return : pitch value *
* About : convert sampling rate to pitch *
* Note : for 8010, sampling rate is at 48kHz, this function should *
* be changed. *
*************************************************************************/
u32 srToPitch(u32 sampleRate)
{
int i;
/* FIXME: These tables should be defined in a headerfile */
static u32 logMagTable[128] = {
0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
};
static char logSlopeTable[128] = {
0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
};
if (sampleRate == 0)
return 0; /* Bail out if no leading "1" */
sampleRate *= 11185; /* Scale 48000 to 0x20002380 */
for (i = 31; i > 0; i--) {
if (sampleRate & 0x80000000) { /* Detect leading "1" */
return (u32) (((s32) (i - 15) << 20) +
logMagTable[0x7f & (sampleRate >> 24)] +
(0x7f & (sampleRate >> 17)) * logSlopeTable[0x7f & (sampleRate >> 24)]);
}
sampleRate = sampleRate << 1;
}
DPF(2, "srToPitch: BUG!\n");
return 0; /* Should never reach this point */
}
/*******************************************
* write/read PCI function 0 registers *
********************************************/
void emu10k1_writefn0(struct emu10k1_card *card, u32 reg, u32 data)
{
unsigned long flags;
if (reg & 0xff000000) {
u32 mask;
u8 size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
mask = ((1 << size) - 1) << offset;
data = (data << offset) & mask;
reg &= 0x7f;
spin_lock_irqsave(&card->lock, flags);
data |= inl(card->iobase + reg) & ~mask;
outl(data, card->iobase + reg);
spin_unlock_irqrestore(&card->lock, flags);
} else {
spin_lock_irqsave(&card->lock, flags);
outl(data, card->iobase + reg);
spin_unlock_irqrestore(&card->lock, flags);
}
return;
}
#ifdef DBGEMU
void emu10k1_writefn0_2(struct emu10k1_card *card, u32 reg, u32 data, int size)
{
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
if (size == 32)
outl(data, card->iobase + (reg & 0x1F));
else if (size == 16)
outw(data, card->iobase + (reg & 0x1F));
else
outb(data, card->iobase + (reg & 0x1F));
spin_unlock_irqrestore(&card->lock, flags);
return;
}
#endif /* DBGEMU */
u32 emu10k1_readfn0(struct emu10k1_card * card, u32 reg)
{
u32 val;
unsigned long flags;
if (reg & 0xff000000) {
u32 mask;
u8 size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
mask = ((1 << size) - 1) << offset;
reg &= 0x7f;
spin_lock_irqsave(&card->lock, flags);
val = inl(card->iobase + reg);
spin_unlock_irqrestore(&card->lock, flags);
return (val & mask) >> offset;
} else {
spin_lock_irqsave(&card->lock, flags);
val = inl(card->iobase + reg);
spin_unlock_irqrestore(&card->lock, flags);
return val;
}
}
void emu10k1_timer_set(struct emu10k1_card * card, u16 data)
{
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
outw(data & TIMER_RATE_MASK, card->iobase + TIMER);
spin_unlock_irqrestore(&card->lock, flags);
}
/************************************************************************
* write/read Emu10k1 pointer-offset register set, accessed through *
* the PTR and DATA registers *
*************************************************************************/
#define A_PTR_ADDRESS_MASK 0x0fff0000
void sblive_writeptr(struct emu10k1_card *card, u32 reg, u32 channel, u32 data)
{
u32 regptr;
unsigned long flags;
regptr = ((reg << 16) & A_PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK);
if (reg & 0xff000000) {
u32 mask;
u8 size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
mask = ((1 << size) - 1) << offset;
data = (data << offset) & mask;
spin_lock_irqsave(&card->lock, flags);
outl(regptr, card->iobase + PTR);
data |= inl(card->iobase + DATA) & ~mask;
outl(data, card->iobase + DATA);
spin_unlock_irqrestore(&card->lock, flags);
} else {
spin_lock_irqsave(&card->lock, flags);
outl(regptr, card->iobase + PTR);
outl(data, card->iobase + DATA);
spin_unlock_irqrestore(&card->lock, flags);
}
}
/* ... : data, reg, ... , TAGLIST_END */
void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...)
{
va_list args;
unsigned long flags;
u32 reg;
va_start(args, channel);
spin_lock_irqsave(&card->lock, flags);
while ((reg = va_arg(args, u32)) != TAGLIST_END) {
u32 data = va_arg(args, u32);
u32 regptr = (((reg << 16) & A_PTR_ADDRESS_MASK)
| (channel & PTR_CHANNELNUM_MASK));
outl(regptr, card->iobase + PTR);
if (reg & 0xff000000) {
int size = (reg >> 24) & 0x3f;
int offset = (reg >> 16) & 0x1f;
u32 mask = ((1 << size) - 1) << offset;
data = (data << offset) & mask;
data |= inl(card->iobase + DATA) & ~mask;
}
outl(data, card->iobase + DATA);
}
spin_unlock_irqrestore(&card->lock, flags);
va_end(args);
return;
}
u32 sblive_readptr(struct emu10k1_card * card, u32 reg, u32 channel)
{
u32 regptr, val;
unsigned long flags;
regptr = ((reg << 16) & A_PTR_ADDRESS_MASK) | (channel & PTR_CHANNELNUM_MASK);
if (reg & 0xff000000) {
u32 mask;
u8 size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
mask = ((1 << size) - 1) << offset;
spin_lock_irqsave(&card->lock, flags);
outl(regptr, card->iobase + PTR);
val = inl(card->iobase + DATA);
spin_unlock_irqrestore(&card->lock, flags);
return (val & mask) >> offset;
} else {
spin_lock_irqsave(&card->lock, flags);
outl(regptr, card->iobase + PTR);
val = inl(card->iobase + DATA);
spin_unlock_irqrestore(&card->lock, flags);
return val;
}
}
void emu10k1_irq_enable(struct emu10k1_card *card, u32 irq_mask)
{
u32 val;
unsigned long flags;
DPF(2,"emu10k1_irq_enable()\n");
spin_lock_irqsave(&card->lock, flags);
val = inl(card->iobase + INTE) | irq_mask;
outl(val, card->iobase + INTE);
spin_unlock_irqrestore(&card->lock, flags);
return;
}
void emu10k1_irq_disable(struct emu10k1_card *card, u32 irq_mask)
{
u32 val;
unsigned long flags;
DPF(2,"emu10k1_irq_disable()\n");
spin_lock_irqsave(&card->lock, flags);
val = inl(card->iobase + INTE) & ~irq_mask;
outl(val, card->iobase + INTE);
spin_unlock_irqrestore(&card->lock, flags);
return;
}
void emu10k1_clear_stop_on_loop(struct emu10k1_card *card, u32 voicenum)
{
/* Voice interrupt */
if (voicenum >= 32)
sblive_writeptr(card, SOLEH | ((0x0100 | (voicenum - 32)) << 16), 0, 0);
else
sblive_writeptr(card, SOLEL | ((0x0100 | voicenum) << 16), 0, 0);
return;
}
static void sblive_wcwait(struct emu10k1_card *card, u32 wait)
{
volatile unsigned uCount;
u32 newtime = 0, curtime;
curtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER);
while (wait--) {
uCount = 0;
while (uCount++ < TIMEOUT) {
newtime = emu10k1_readfn0(card, WC_SAMPLECOUNTER);
if (newtime != curtime)
break;
}
if (uCount >= TIMEOUT)
break;
curtime = newtime;
}
}
u16 emu10k1_ac97_read(struct ac97_codec *codec, u8 reg)
{
struct emu10k1_card *card = codec->private_data;
u16 data;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
outb(reg, card->iobase + AC97ADDRESS);
data = inw(card->iobase + AC97DATA);
spin_unlock_irqrestore(&card->lock, flags);
return data;
}
void emu10k1_ac97_write(struct ac97_codec *codec, u8 reg, u16 value)
{
struct emu10k1_card *card = codec->private_data;
unsigned long flags;
spin_lock_irqsave(&card->lock, flags);
outb(reg, card->iobase + AC97ADDRESS);
outw(value, card->iobase + AC97DATA);
outb( AC97_EXTENDED_ID, card->iobase + AC97ADDRESS);
spin_unlock_irqrestore(&card->lock, flags);
}
/*********************************************************
* MPU access functions *
**********************************************************/
int emu10k1_mpu_write_data(struct emu10k1_card *card, u8 data)
{
unsigned long flags;
int ret;
if (card->is_audigy) {
if ((sblive_readptr(card, A_MUSTAT,0) & MUSTAT_ORDYN) == 0) {
sblive_writeptr(card, A_MUDATA, 0, data);
ret = 0;
} else
ret = -1;
} else {
spin_lock_irqsave(&card->lock, flags);
if ((inb(card->iobase + MUSTAT) & MUSTAT_ORDYN) == 0) {
outb(data, card->iobase + MUDATA);
ret = 0;
} else
ret = -1;
spin_unlock_irqrestore(&card->lock, flags);
}
return ret;
}
int emu10k1_mpu_read_data(struct emu10k1_card *card, u8 * data)
{
unsigned long flags;
int ret;
if (card->is_audigy) {
if ((sblive_readptr(card, A_MUSTAT,0) & MUSTAT_IRDYN) == 0) {
*data = sblive_readptr(card, A_MUDATA,0);
ret = 0;
} else
ret = -1;
} else {
spin_lock_irqsave(&card->lock, flags);
if ((inb(card->iobase + MUSTAT) & MUSTAT_IRDYN) == 0) {
*data = inb(card->iobase + MUDATA);
ret = 0;
} else
ret = -1;
spin_unlock_irqrestore(&card->lock, flags);
}
return ret;
}
int emu10k1_mpu_reset(struct emu10k1_card *card)
{
u8 status;
unsigned long flags;
DPF(2, "emu10k1_mpu_reset()\n");
if (card->is_audigy) {
if (card->mpuacqcount == 0) {
sblive_writeptr(card, A_MUCMD, 0, MUCMD_RESET);
sblive_wcwait(card, 8);
sblive_writeptr(card, A_MUCMD, 0, MUCMD_RESET);
sblive_wcwait(card, 8);
sblive_writeptr(card, A_MUCMD, 0, MUCMD_ENTERUARTMODE);
sblive_wcwait(card, 8);
status = sblive_readptr(card, A_MUDATA, 0);
if (status == 0xfe)
return 0;
else
return -1;
}
return 0;
} else {
if (card->mpuacqcount == 0) {
spin_lock_irqsave(&card->lock, flags);
outb(MUCMD_RESET, card->iobase + MUCMD);
spin_unlock_irqrestore(&card->lock, flags);
sblive_wcwait(card, 8);
spin_lock_irqsave(&card->lock, flags);
outb(MUCMD_RESET, card->iobase + MUCMD);
spin_unlock_irqrestore(&card->lock, flags);
sblive_wcwait(card, 8);
spin_lock_irqsave(&card->lock, flags);
outb(MUCMD_ENTERUARTMODE, card->iobase + MUCMD);
spin_unlock_irqrestore(&card->lock, flags);
sblive_wcwait(card, 8);
spin_lock_irqsave(&card->lock, flags);
status = inb(card->iobase + MUDATA);
spin_unlock_irqrestore(&card->lock, flags);
if (status == 0xfe)
return 0;
else
return -1;
}
return 0;
}
}
int emu10k1_mpu_acquire(struct emu10k1_card *card)
{
/* FIXME: This should be a macro */
++card->mpuacqcount;
return 0;
}
int emu10k1_mpu_release(struct emu10k1_card *card)
{
/* FIXME: this should be a macro */
--card->mpuacqcount;
return 0;
}

View File

@@ -0,0 +1,247 @@
/*
**********************************************************************
* hwaccess.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _HWACCESS_H
#define _HWACCESS_H
#include <linux/fs.h>
#include <linux/sound.h>
#include <linux/soundcard.h>
#include <linux/ac97_codec.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/io.h>
#include "efxmgr.h"
#include "passthrough.h"
#include "midi.h"
#define EMUPAGESIZE 4096 /* don't change */
#define NUM_G 64 /* use all channels */
#define NUM_FXSENDS 4 /* don't change */
/* setting this to other than a power of two may break some applications */
#define MAXBUFSIZE 65536
#define MAXPAGES 8192
#define BUFMAXPAGES (MAXBUFSIZE / PAGE_SIZE)
#define FLAGS_AVAILABLE 0x0001
#define FLAGS_READY 0x0002
struct memhandle
{
dma_addr_t dma_handle;
void *addr;
u32 size;
};
#define DEBUG_LEVEL 2
#ifdef EMU10K1_DEBUG
# define DPD(level,x,y...) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ , y );} while(0)
# define DPF(level,x) do {if(level <= DEBUG_LEVEL) printk( KERN_NOTICE "emu10k1: %s: %d: " x , __FILE__ , __LINE__ );} while(0)
#else
# define DPD(level,x,y...) do { } while (0) /* not debugging: nothing */
# define DPF(level,x) do { } while (0)
#endif /* EMU10K1_DEBUG */
#define ERROR() DPF(1,"error\n")
/* DATA STRUCTURES */
struct emu10k1_waveout
{
u32 send_routing[3];
// audigy only:
u32 send_routing2[3];
u32 send_dcba[3];
// audigy only:
u32 send_hgfe[3];
};
#define ROUTE_PCM 0
#define ROUTE_PT 1
#define ROUTE_PCM1 2
#define SEND_MONO 0
#define SEND_LEFT 1
#define SEND_RIGHT 2
struct emu10k1_wavein
{
struct wiinst *ac97;
struct wiinst *mic;
struct wiinst *fx;
u8 recsrc;
u32 fxwc;
};
#define CMD_READ 1
#define CMD_WRITE 2
struct mixer_private_ioctl {
u32 cmd;
u32 val[90];
};
/* bogus ioctls numbers to escape from OSS mixer limitations */
#define CMD_WRITEFN0 _IOW('D', 0, struct mixer_private_ioctl)
#define CMD_READFN0 _IOR('D', 1, struct mixer_private_ioctl)
#define CMD_WRITEPTR _IOW('D', 2, struct mixer_private_ioctl)
#define CMD_READPTR _IOR('D', 3, struct mixer_private_ioctl)
#define CMD_SETRECSRC _IOW('D', 4, struct mixer_private_ioctl)
#define CMD_GETRECSRC _IOR('D', 5, struct mixer_private_ioctl)
#define CMD_GETVOICEPARAM _IOR('D', 6, struct mixer_private_ioctl)
#define CMD_SETVOICEPARAM _IOW('D', 7, struct mixer_private_ioctl)
#define CMD_GETPATCH _IOR('D', 8, struct mixer_private_ioctl)
#define CMD_GETGPR _IOR('D', 9, struct mixer_private_ioctl)
#define CMD_GETCTLGPR _IOR('D', 10, struct mixer_private_ioctl)
#define CMD_SETPATCH _IOW('D', 11, struct mixer_private_ioctl)
#define CMD_SETGPR _IOW('D', 12, struct mixer_private_ioctl)
#define CMD_SETCTLGPR _IOW('D', 13, struct mixer_private_ioctl)
#define CMD_SETGPOUT _IOW('D', 14, struct mixer_private_ioctl)
#define CMD_GETGPR2OSS _IOR('D', 15, struct mixer_private_ioctl)
#define CMD_SETGPR2OSS _IOW('D', 16, struct mixer_private_ioctl)
#define CMD_SETMCH_FX _IOW('D', 17, struct mixer_private_ioctl)
#define CMD_SETPASSTHROUGH _IOW('D', 18, struct mixer_private_ioctl)
#define CMD_PRIVATE3_VERSION _IOW('D', 19, struct mixer_private_ioctl)
#define CMD_AC97_BOOST _IOW('D', 20, struct mixer_private_ioctl)
//up this number when breaking compatibility
#define PRIVATE3_VERSION 2
struct emu10k1_card
{
struct list_head list;
struct memhandle virtualpagetable;
struct memhandle tankmem;
struct memhandle silentpage;
spinlock_t lock;
u8 voicetable[NUM_G];
u16 emupagetable[MAXPAGES];
struct list_head timers;
u16 timer_delay;
spinlock_t timer_lock;
struct pci_dev *pci_dev;
unsigned long iobase;
unsigned long length;
unsigned short model;
unsigned int irq;
int audio_dev;
int audio_dev1;
int midi_dev;
#ifdef EMU10K1_SEQUENCER
int seq_dev;
struct emu10k1_mididevice *seq_mididev;
#endif
struct ac97_codec *ac97;
int ac97_supported_mixers;
int ac97_stereo_mixers;
/* Number of first fx voice for multichannel output */
u8 mchannel_fx;
struct emu10k1_waveout waveout;
struct emu10k1_wavein wavein;
struct emu10k1_mpuout *mpuout;
struct emu10k1_mpuin *mpuin;
struct semaphore open_sem;
mode_t open_mode;
wait_queue_head_t open_wait;
u32 mpuacqcount; // Mpu acquire count
u32 has_toslink; // TOSLink detection
u8 chiprev; /* Chip revision */
u8 is_audigy;
u8 is_aps;
struct patch_manager mgr;
struct pt_data pt;
};
int emu10k1_addxmgr_alloc(u32, struct emu10k1_card *);
void emu10k1_addxmgr_free(struct emu10k1_card *, int);
int emu10k1_find_control_gpr(struct patch_manager *, const char *, const char *);
void emu10k1_set_control_gpr(struct emu10k1_card *, int , s32, int );
void emu10k1_set_volume_gpr(struct emu10k1_card *, int, s32, int);
#define VOL_6BIT 0x40
#define VOL_5BIT 0x20
#define VOL_4BIT 0x10
#define TIMEOUT 16384
u32 srToPitch(u32);
extern struct list_head emu10k1_devs;
/* Hardware Abstraction Layer access functions */
void emu10k1_writefn0(struct emu10k1_card *, u32, u32);
void emu10k1_writefn0_2(struct emu10k1_card *, u32, u32, int);
u32 emu10k1_readfn0(struct emu10k1_card *, u32);
void emu10k1_timer_set(struct emu10k1_card *, u16);
void sblive_writeptr(struct emu10k1_card *, u32, u32, u32);
void sblive_writeptr_tag(struct emu10k1_card *, u32, ...);
#define TAGLIST_END 0
u32 sblive_readptr(struct emu10k1_card *, u32 , u32 );
void emu10k1_irq_enable(struct emu10k1_card *, u32);
void emu10k1_irq_disable(struct emu10k1_card *, u32);
void emu10k1_clear_stop_on_loop(struct emu10k1_card *, u32);
/* AC97 Codec register access function */
u16 emu10k1_ac97_read(struct ac97_codec *, u8);
void emu10k1_ac97_write(struct ac97_codec *, u8, u16);
/* MPU access function*/
int emu10k1_mpu_write_data(struct emu10k1_card *, u8);
int emu10k1_mpu_read_data(struct emu10k1_card *, u8 *);
int emu10k1_mpu_reset(struct emu10k1_card *);
int emu10k1_mpu_acquire(struct emu10k1_card *);
int emu10k1_mpu_release(struct emu10k1_card *);
#endif /* _HWACCESS_H */

View File

@@ -0,0 +1,163 @@
/*
**********************************************************************
* isblive_mid.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _ICARDMIDI_H
#define _ICARDMIDI_H
/* MIDI defines */
#define MIDI_DATA_FIRST 0x00
#define MIDI_DATA_LAST 0x7F
#define MIDI_STATUS_FIRST 0x80
#define MIDI_STATUS_LAST 0xFF
/* Channel status bytes */
#define MIDI_STATUS_CHANNEL_FIRST 0x80
#define MIDI_STATUS_CHANNEL_LAST 0xE0
#define MIDI_STATUS_CHANNEL_MASK 0xF0
/* Channel voice messages */
#define MIDI_VOICE_NOTE_OFF 0x80
#define MIDI_VOICE_NOTE_ON 0x90
#define MIDI_VOICE_POLY_PRESSURE 0xA0
#define MIDI_VOICE_CONTROL_CHANGE 0xB0
#define MIDI_VOICE_PROGRAM_CHANGE 0xC0
#define MIDI_VOICE_CHANNEL_PRESSURE 0xD0
#define MIDI_VOICE_PITCH_BEND 0xE0
/* Channel mode messages */
#define MIDI_MODE_CHANNEL MIDI_VOICE_CONTROL_CHANGE
/* System status bytes */
#define MIDI_STATUS_SYSTEM_FIRST 0xF0
#define MIDI_STATUS_SYSTEM_LAST 0xFF
/* System exclusive messages */
#define MIDI_SYSEX_BEGIN 0xF0
#define MIDI_SYSEX_EOX 0xF7
/* System common messages */
#define MIDI_COMMON_TCQF 0xF1 /* Time code quarter frame */
#define MIDI_COMMON_SONG_POSITION 0xF2
#define MIDI_COMMON_SONG_SELECT 0xF3
#define MIDI_COMMON_UNDEFINED_F4 0xF4
#define MIDI_COMMON_UNDEFINED_F5 0xF5
#define MIDI_COMMON_TUNE_REQUEST 0xF6
/* System real-time messages */
#define MIDI_RTIME_TIMING_CLOCK 0xF8
#define MIDI_RTIME_UNDEFINED_F9 0xF9
#define MIDI_RTIME_START 0xFA
#define MIDI_RTIME_CONTINUE 0xFB
#define MIDI_RTIME_STOP 0xFC
#define MIDI_RTIME_UNDEFINED_FD 0xFD
#define MIDI_RTIME_ACTIVE_SENSING 0xFE
#define MIDI_RTIME_SYSTEM_RESET 0xFF
/* Flags for flags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */
#define MIDI_CACHE_ALL 1
#define MIDI_CACHE_BESTFIT 2
#define MIDI_CACHE_QUERY 3
#define MIDI_UNCACHE 4
/* Event declarations for MPU IRQ Callbacks */
#define ICARDMIDI_INLONGDATA 0x00000001 /* MIM_LONGDATA */
#define ICARDMIDI_INLONGERROR 0x00000002 /* MIM_LONGERROR */
#define ICARDMIDI_OUTLONGDATA 0x00000004 /* MOM_DONE for MPU OUT buffer */
#define ICARDMIDI_INDATA 0x00000010 /* MIM_DATA */
#define ICARDMIDI_INDATAERROR 0x00000020 /* MIM_ERROR */
/* Declaration for flags in CARDMIDIBUFFERHDR */
/* Make it the same as MHDR_DONE, MHDR_INQUEUE in mmsystem.h */
#define MIDIBUF_DONE 0x00000001
#define MIDIBUF_INQUEUE 0x00000004
/* Declaration for msg parameter in midiCallbackFn */
#define ICARDMIDI_OUTBUFFEROK 0x00000001
#define ICARDMIDI_INMIDIOK 0x00000002
/* Declaration for technology in struct midi_caps */
#define MT_MIDIPORT 0x00000001 /* In original MIDIOUTCAPS structure */
#define MT_FMSYNTH 0x00000004 /* In original MIDIOUTCAPS structure */
#define MT_AWESYNTH 0x00001000
#define MT_PCISYNTH 0x00002000
#define MT_PCISYNTH64 0x00004000
#define CARDMIDI_AWEMASK 0x0000F000
enum LocalErrorCode
{
CTSTATUS_NOTENABLED = 0x7000,
CTSTATUS_READY,
CTSTATUS_BUSY,
CTSTATUS_DATAAVAIL,
CTSTATUS_NODATA,
CTSTATUS_NEXT_BYTE
};
/* MIDI data block header */
struct midi_hdr
{
u8 *reserved; /* Pointer to original locked data block */
u32 bufferlength; /* Length of data in data block */
u32 bytesrecorded; /* Used for input only */
u32 user; /* For client's use */
u32 flags; /* Assorted flags (see defines) */
struct list_head list; /* Reserved for driver */
u8 *data; /* Second copy of first pointer */
};
/* Enumeration for SetControl */
enum
{
MIDIOBJVOLUME = 0x1,
MIDIQUERYACTIVEINST
};
struct midi_queue
{
struct midi_queue *next;
u32 qtype; /* 0 = short message, 1 = long data */
u32 length;
u32 sizeLeft;
u8 *midibyte;
unsigned long refdata;
};
struct midi_openinfo
{
u32 cbsize;
u32 flags;
unsigned long refdata;
u32 streamid;
};
int emu10k1_midi_callback(unsigned long , unsigned long, unsigned long *);
#endif /* _ICARDMIDI_H */

View File

@@ -0,0 +1,53 @@
/*
**********************************************************************
* icardwav.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _ICARDWAV_H
#define _ICARDWAV_H
struct wave_format
{
int id;
int samplingrate;
u8 bitsperchannel;
u8 channels; /* 1 = Mono, 2 = Stereo, 3, ... = Multichannel */
u8 bytesperchannel;
u8 bytespervoicesample;
u8 bytespersample;
int bytespersec;
u8 passthrough;
};
/* emu10k1_wave states */
#define WAVE_STATE_OPEN 0x01
#define WAVE_STATE_STARTED 0x02
#define WAVE_STATE_CLOSED 0x04
#endif /* _ICARDWAV_H */

113
sound/oss/emu10k1/irqmgr.c Normal file
View File

@@ -0,0 +1,113 @@
/*
**********************************************************************
* irqmgr.c - IRQ manager for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include "hwaccess.h"
#include "8010.h"
#include "cardmi.h"
#include "cardmo.h"
#include "irqmgr.h"
/* Interrupt handler */
irqreturn_t emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct emu10k1_card *card = (struct emu10k1_card *) dev_id;
u32 irqstatus, irqstatus_tmp;
int handled = 0;
DPD(4, "emu10k1_interrupt called, irq = %u\n", irq);
/*
** NOTE :
** We do a 'while loop' here cos on certain machines, with both
** playback and recording going on at the same time, IRQs will
** stop coming in after a while. Checking IPND indeed shows that
** there are interrupts pending but the PIC says no IRQs pending.
** I suspect that some boards need edge-triggered IRQs but are not
** getting that condition if we don't completely clear the IPND
** (make sure no more interrupts are pending).
** - Eric
*/
while ((irqstatus = inl(card->iobase + IPR))) {
DPD(4, "irq status %#x\n", irqstatus);
irqstatus_tmp = irqstatus;
if (irqstatus & IRQTYPE_TIMER) {
emu10k1_timer_irqhandler(card);
irqstatus &= ~IRQTYPE_TIMER;
}
if (irqstatus & IRQTYPE_DSP) {
emu10k1_dsp_irqhandler(card);
irqstatus &= ~IRQTYPE_DSP;
}
if (irqstatus & IRQTYPE_MPUIN) {
emu10k1_mpuin_irqhandler(card);
irqstatus &= ~IRQTYPE_MPUIN;
}
if (irqstatus & IRQTYPE_MPUOUT) {
emu10k1_mpuout_irqhandler(card);
irqstatus &= ~IRQTYPE_MPUOUT;
}
if (irqstatus & IPR_MUTE) {
emu10k1_mute_irqhandler(card);
irqstatus &=~IPR_MUTE;
}
if (irqstatus & IPR_VOLINCR) {
emu10k1_volincr_irqhandler(card);
irqstatus &=~IPR_VOLINCR;
}
if (irqstatus & IPR_VOLDECR) {
emu10k1_voldecr_irqhandler(card);
irqstatus &=~IPR_VOLDECR;
}
if (irqstatus){
printk(KERN_ERR "emu10k1: Warning, unhandled interrupt: %#08x\n", irqstatus);
//make sure any interrupts we don't handle are disabled:
emu10k1_irq_disable(card, ~(INTE_MIDIRXENABLE | INTE_MIDITXENABLE | INTE_INTERVALTIMERENB |
INTE_VOLDECRENABLE | INTE_VOLINCRENABLE | INTE_MUTEENABLE |
INTE_FXDSPENABLE));
}
/* acknowledge interrupt */
outl(irqstatus_tmp, card->iobase + IPR);
handled = 1;
}
return IRQ_RETVAL(handled);
}

View File

@@ -0,0 +1,52 @@
/*
**********************************************************************
* irq.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _IRQ_H
#define _IRQ_H
/* EMU Irq Types */
#define IRQTYPE_PCIBUSERROR IPR_PCIERROR
#define IRQTYPE_MIXERBUTTON (IPR_VOLINCR | IPR_VOLDECR | IPR_MUTE)
#define IRQTYPE_VOICE (IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK)
#define IRQTYPE_RECORD (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL | IPR_MICBUFFULL | IPR_MICBUFHALFFULL | IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)
#define IRQTYPE_MPUOUT (IPR_MIDITRANSBUFEMPTY | A_IPR_MIDITRANSBUFEMPTY2)
#define IRQTYPE_MPUIN (IPR_MIDIRECVBUFEMPTY | A_IPR_MIDIRECVBUFEMPTY2)
#define IRQTYPE_TIMER IPR_INTERVALTIMER
#define IRQTYPE_SPDIF (IPR_GPSPDIFSTATUSCHANGE | IPR_CDROMSTATUSCHANGE)
#define IRQTYPE_DSP IPR_FXDSP
void emu10k1_timer_irqhandler(struct emu10k1_card *);
void emu10k1_dsp_irqhandler(struct emu10k1_card *);
void emu10k1_mute_irqhandler(struct emu10k1_card *);
void emu10k1_volincr_irqhandler(struct emu10k1_card *);
void emu10k1_voldecr_irqhandler(struct emu10k1_card *);
#endif /* _IRQ_H */

1475
sound/oss/emu10k1/main.c Normal file

File diff suppressed because it is too large Load Diff

613
sound/oss/emu10k1/midi.c Normal file
View File

@@ -0,0 +1,613 @@
/*
**********************************************************************
* midi.c - /dev/midi interface for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include "hwaccess.h"
#include "cardmo.h"
#include "cardmi.h"
#include "midi.h"
#ifdef EMU10K1_SEQUENCER
#include "../sound_config.h"
#endif
static DEFINE_SPINLOCK(midi_spinlock __attribute((unused)));
static void init_midi_hdr(struct midi_hdr *midihdr)
{
midihdr->bufferlength = MIDIIN_BUFLEN;
midihdr->bytesrecorded = 0;
midihdr->flags = 0;
}
static int midiin_add_buffer(struct emu10k1_mididevice *midi_dev, struct midi_hdr **midihdrptr)
{
struct midi_hdr *midihdr;
if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL) {
ERROR();
return -EINVAL;
}
init_midi_hdr(midihdr);
if ((midihdr->data = (u8 *) kmalloc(MIDIIN_BUFLEN, GFP_KERNEL)) == NULL) {
ERROR();
kfree(midihdr);
return -1;
}
if (emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr) < 0) {
ERROR();
kfree(midihdr->data);
kfree(midihdr);
return -1;
}
*midihdrptr = midihdr;
list_add_tail(&midihdr->list, &midi_dev->mid_hdrs);
return 0;
}
static int emu10k1_midi_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct emu10k1_card *card = NULL;
struct emu10k1_mididevice *midi_dev;
struct list_head *entry;
DPF(2, "emu10k1_midi_open()\n");
/* Check for correct device to open */
list_for_each(entry, &emu10k1_devs) {
card = list_entry(entry, struct emu10k1_card, list);
if (card->midi_dev == minor)
goto match;
}
return -ENODEV;
match:
#ifdef EMU10K1_SEQUENCER
if (card->seq_mididev) /* card is opened by sequencer */
return -EBUSY;
#endif
/* Wait for device to become free */
down(&card->open_sem);
while (card->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
if (file->f_flags & O_NONBLOCK) {
up(&card->open_sem);
return -EBUSY;
}
up(&card->open_sem);
interruptible_sleep_on(&card->open_wait);
if (signal_pending(current)) {
return -ERESTARTSYS;
}
down(&card->open_sem);
}
if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
return -EINVAL;
midi_dev->card = card;
midi_dev->mistate = MIDIIN_STATE_STOPPED;
init_waitqueue_head(&midi_dev->oWait);
init_waitqueue_head(&midi_dev->iWait);
midi_dev->ird = 0;
midi_dev->iwr = 0;
midi_dev->icnt = 0;
INIT_LIST_HEAD(&midi_dev->mid_hdrs);
if (file->f_mode & FMODE_READ) {
struct midi_openinfo dsCardMidiOpenInfo;
struct midi_hdr *midihdr1;
struct midi_hdr *midihdr2;
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
if (emu10k1_mpuin_open(card, &dsCardMidiOpenInfo) < 0) {
ERROR();
kfree(midi_dev);
return -ENODEV;
}
/* Add two buffers to receive sysex buffer */
if (midiin_add_buffer(midi_dev, &midihdr1) < 0) {
kfree(midi_dev);
return -ENODEV;
}
if (midiin_add_buffer(midi_dev, &midihdr2) < 0) {
list_del(&midihdr1->list);
kfree(midihdr1->data);
kfree(midihdr1);
kfree(midi_dev);
return -ENODEV;
}
}
if (file->f_mode & FMODE_WRITE) {
struct midi_openinfo dsCardMidiOpenInfo;
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
ERROR();
kfree(midi_dev);
return -ENODEV;
}
}
file->private_data = (void *) midi_dev;
card->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
up(&card->open_sem);
return nonseekable_open(inode, file);
}
static int emu10k1_midi_release(struct inode *inode, struct file *file)
{
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
struct emu10k1_card *card;
lock_kernel();
card = midi_dev->card;
DPF(2, "emu10k1_midi_release()\n");
if (file->f_mode & FMODE_WRITE) {
if (!(file->f_flags & O_NONBLOCK)) {
while (!signal_pending(current) && (card->mpuout->firstmidiq != NULL)) {
DPF(4, "Cannot close - buffers not empty\n");
interruptible_sleep_on(&midi_dev->oWait);
}
}
emu10k1_mpuout_close(card);
}
if (file->f_mode & FMODE_READ) {
struct midi_hdr *midihdr;
if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
emu10k1_mpuin_stop(card);
midi_dev->mistate = MIDIIN_STATE_STOPPED;
}
emu10k1_mpuin_reset(card);
emu10k1_mpuin_close(card);
while (!list_empty(&midi_dev->mid_hdrs)) {
midihdr = list_entry(midi_dev->mid_hdrs.next, struct midi_hdr, list);
list_del(midi_dev->mid_hdrs.next);
kfree(midihdr->data);
kfree(midihdr);
}
}
kfree(midi_dev);
down(&card->open_sem);
card->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE));
up(&card->open_sem);
wake_up_interruptible(&card->open_wait);
unlock_kernel();
return 0;
}
static ssize_t emu10k1_midi_read(struct file *file, char __user *buffer, size_t count, loff_t * pos)
{
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
ssize_t ret = 0;
u16 cnt;
unsigned long flags;
DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count);
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (midi_dev->mistate == MIDIIN_STATE_STOPPED) {
if (emu10k1_mpuin_start(midi_dev->card) < 0) {
ERROR();
return -EINVAL;
}
midi_dev->mistate = MIDIIN_STATE_STARTED;
}
while (count > 0) {
cnt = MIDIIN_BUFLEN - midi_dev->ird;
spin_lock_irqsave(&midi_spinlock, flags);
if (midi_dev->icnt < cnt)
cnt = midi_dev->icnt;
spin_unlock_irqrestore(&midi_spinlock, flags);
if (cnt > count)
cnt = count;
if (cnt <= 0) {
if (file->f_flags & O_NONBLOCK)
return ret ? ret : -EAGAIN;
DPF(2, " Go to sleep...\n");
interruptible_sleep_on(&midi_dev->iWait);
if (signal_pending(current))
return ret ? ret : -ERESTARTSYS;
continue;
}
if (copy_to_user(buffer, midi_dev->iBuf + midi_dev->ird, cnt)) {
ERROR();
return ret ? ret : -EFAULT;
}
midi_dev->ird += cnt;
midi_dev->ird %= MIDIIN_BUFLEN;
spin_lock_irqsave(&midi_spinlock, flags);
midi_dev->icnt -= cnt;
spin_unlock_irqrestore(&midi_spinlock, flags);
count -= cnt;
buffer += cnt;
ret += cnt;
if (midi_dev->icnt == 0)
break;
}
return ret;
}
static ssize_t emu10k1_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t * pos)
{
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
struct midi_hdr *midihdr;
unsigned long flags;
DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count);
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
return -EINVAL;
midihdr->bufferlength = count;
midihdr->bytesrecorded = 0;
midihdr->flags = 0;
if ((midihdr->data = (u8 *) kmalloc(count, GFP_KERNEL)) == NULL) {
ERROR();
kfree(midihdr);
return -EINVAL;
}
if (copy_from_user(midihdr->data, buffer, count)) {
kfree(midihdr->data);
kfree(midihdr);
return -EFAULT;
}
spin_lock_irqsave(&midi_spinlock, flags);
if (emu10k1_mpuout_add_buffer(midi_dev->card, midihdr) < 0) {
ERROR();
kfree(midihdr->data);
kfree(midihdr);
spin_unlock_irqrestore(&midi_spinlock, flags);
return -EINVAL;
}
spin_unlock_irqrestore(&midi_spinlock, flags);
return count;
}
static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait)
{
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
unsigned long flags;
unsigned int mask = 0;
DPF(4, "emu10k1_midi_poll() called\n");
if (file->f_mode & FMODE_WRITE)
poll_wait(file, &midi_dev->oWait, wait);
if (file->f_mode & FMODE_READ)
poll_wait(file, &midi_dev->iWait, wait);
spin_lock_irqsave(&midi_spinlock, flags);
if (file->f_mode & FMODE_WRITE)
mask |= POLLOUT | POLLWRNORM;
if (file->f_mode & FMODE_READ) {
if (midi_dev->mistate == MIDIIN_STATE_STARTED)
if (midi_dev->icnt > 0)
mask |= POLLIN | POLLRDNORM;
}
spin_unlock_irqrestore(&midi_spinlock, flags);
return mask;
}
int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg)
{
struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) refdata;
struct midi_hdr *midihdr = NULL;
unsigned long flags;
int i;
DPF(4, "emu10k1_midi_callback()\n");
spin_lock_irqsave(&midi_spinlock, flags);
switch (msg) {
case ICARDMIDI_OUTLONGDATA:
midihdr = (struct midi_hdr *) pmsg[2];
kfree(midihdr->data);
kfree(midihdr);
wake_up_interruptible(&midi_dev->oWait);
break;
case ICARDMIDI_INLONGDATA:
midihdr = (struct midi_hdr *) pmsg[2];
for (i = 0; i < midihdr->bytesrecorded; i++) {
midi_dev->iBuf[midi_dev->iwr++] = midihdr->data[i];
midi_dev->iwr %= MIDIIN_BUFLEN;
}
midi_dev->icnt += midihdr->bytesrecorded;
if (midi_dev->mistate == MIDIIN_STATE_STARTED) {
init_midi_hdr(midihdr);
emu10k1_mpuin_add_buffer(midi_dev->card->mpuin, midihdr);
wake_up_interruptible(&midi_dev->iWait);
}
break;
case ICARDMIDI_INDATA:
{
u8 *pBuf = (u8 *) & pmsg[1];
u16 bytesvalid = pmsg[2];
for (i = 0; i < bytesvalid; i++) {
midi_dev->iBuf[midi_dev->iwr++] = pBuf[i];
midi_dev->iwr %= MIDIIN_BUFLEN;
}
midi_dev->icnt += bytesvalid;
}
wake_up_interruptible(&midi_dev->iWait);
break;
default: /* Unknown message */
spin_unlock_irqrestore(&midi_spinlock, flags);
return -1;
}
spin_unlock_irqrestore(&midi_spinlock, flags);
return 0;
}
/* MIDI file operations */
struct file_operations emu10k1_midi_fops = {
.owner = THIS_MODULE,
.read = emu10k1_midi_read,
.write = emu10k1_midi_write,
.poll = emu10k1_midi_poll,
.open = emu10k1_midi_open,
.release = emu10k1_midi_release,
};
#ifdef EMU10K1_SEQUENCER
/* functions used for sequencer access */
int emu10k1_seq_midi_open(int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev))
{
struct emu10k1_card *card;
struct midi_openinfo dsCardMidiOpenInfo;
struct emu10k1_mididevice *midi_dev;
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
return -EINVAL;
card = midi_devs[dev]->devc;
if (card->open_mode) /* card is opened native */
return -EBUSY;
DPF(2, "emu10k1_seq_midi_open()\n");
if ((midi_dev = (struct emu10k1_mididevice *) kmalloc(sizeof(*midi_dev), GFP_KERNEL)) == NULL)
return -EINVAL;
midi_dev->card = card;
midi_dev->mistate = MIDIIN_STATE_STOPPED;
init_waitqueue_head(&midi_dev->oWait);
init_waitqueue_head(&midi_dev->iWait);
midi_dev->ird = 0;
midi_dev->iwr = 0;
midi_dev->icnt = 0;
INIT_LIST_HEAD(&midi_dev->mid_hdrs);
dsCardMidiOpenInfo.refdata = (unsigned long) midi_dev;
if (emu10k1_mpuout_open(card, &dsCardMidiOpenInfo) < 0) {
ERROR();
return -ENODEV;
}
card->seq_mididev = midi_dev;
return 0;
}
void emu10k1_seq_midi_close(int dev)
{
struct emu10k1_card *card;
DPF(2, "emu10k1_seq_midi_close()\n");
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
return;
card = midi_devs[dev]->devc;
emu10k1_mpuout_close(card);
if (card->seq_mididev) {
kfree(card->seq_mididev);
card->seq_mididev = NULL;
}
}
int emu10k1_seq_midi_out(int dev, unsigned char midi_byte)
{
struct emu10k1_card *card;
struct midi_hdr *midihdr;
unsigned long flags;
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
return -EINVAL;
card = midi_devs[dev]->devc;
if ((midihdr = (struct midi_hdr *) kmalloc(sizeof(struct midi_hdr), GFP_KERNEL)) == NULL)
return -EINVAL;
midihdr->bufferlength = 1;
midihdr->bytesrecorded = 0;
midihdr->flags = 0;
if ((midihdr->data = (u8 *) kmalloc(1, GFP_KERNEL)) == NULL) {
ERROR();
kfree(midihdr);
return -EINVAL;
}
*(midihdr->data) = midi_byte;
spin_lock_irqsave(&midi_spinlock, flags);
if (emu10k1_mpuout_add_buffer(card, midihdr) < 0) {
ERROR();
kfree(midihdr->data);
kfree(midihdr);
spin_unlock_irqrestore(&midi_spinlock, flags);
return -EINVAL;
}
spin_unlock_irqrestore(&midi_spinlock, flags);
return 1;
}
int emu10k1_seq_midi_start_read(int dev)
{
return 0;
}
int emu10k1_seq_midi_end_read(int dev)
{
return 0;
}
void emu10k1_seq_midi_kick(int dev)
{
}
int emu10k1_seq_midi_buffer_status(int dev)
{
int count;
struct midi_queue *queue;
struct emu10k1_card *card;
if (midi_devs[dev] == NULL || midi_devs[dev]->devc == NULL)
return -EINVAL;
count = 0;
card = midi_devs[dev]->devc;
queue = card->mpuout->firstmidiq;
while (queue != NULL) {
count++;
if (queue == card->mpuout->lastmidiq)
break;
queue = queue->next;
}
return count;
}
#endif

78
sound/oss/emu10k1/midi.h Normal file
View File

@@ -0,0 +1,78 @@
/*
**********************************************************************
* midi.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _MIDI_H
#define _MIDI_H
#define FMODE_MIDI_SHIFT 3
#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)
#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
#define MIDIIN_STATE_STARTED 0x00000001
#define MIDIIN_STATE_STOPPED 0x00000002
#define MIDIIN_BUFLEN 1024
struct emu10k1_mididevice
{
struct emu10k1_card *card;
u32 mistate;
wait_queue_head_t oWait;
wait_queue_head_t iWait;
s8 iBuf[MIDIIN_BUFLEN];
u16 ird, iwr, icnt;
struct list_head mid_hdrs;
};
/* uncomment next line to use midi port on Audigy drive */
//#define USE_AUDIGY_DRIVE_MIDI
#ifdef USE_AUDIGY_DRIVE_MIDI
#define A_MUDATA A_MUDATA2
#define A_MUCMD A_MUCMD2
#define A_MUSTAT A_MUCMD2
#define A_IPR_MIDITRANSBUFEMPTY A_IPR_MIDITRANSBUFEMPTY2
#define A_IPR_MIDIRECVBUFEMPTY A_IPR_MIDIRECVBUFEMPTY2
#define A_INTE_MIDITXENABLE A_INTE_MIDITXENABLE2
#define A_INTE_MIDIRXENABLE A_INTE_MIDIRXENABLE2
#else
#define A_MUDATA A_MUDATA1
#define A_MUCMD A_MUCMD1
#define A_MUSTAT A_MUCMD1
#define A_IPR_MIDITRANSBUFEMPTY A_IPR_MIDITRANSBUFEMPTY1
#define A_IPR_MIDIRECVBUFEMPTY A_IPR_MIDIRECVBUFEMPTY1
#define A_INTE_MIDITXENABLE A_INTE_MIDITXENABLE1
#define A_INTE_MIDIRXENABLE A_INTE_MIDIRXENABLE1
#endif
#endif /* _MIDI_H */

690
sound/oss/emu10k1/mixer.c Normal file
View File

@@ -0,0 +1,690 @@
/*
**********************************************************************
* mixer.c - /dev/mixer interface for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up stuff
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include "hwaccess.h"
#include "8010.h"
#include "recmgr.h"
static const u32 bass_table[41][5] = {
{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
{ 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
{ 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
{ 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
{ 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
{ 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
{ 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
{ 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
{ 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
{ 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
{ 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
{ 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
{ 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
{ 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
{ 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
{ 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
{ 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
{ 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
{ 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
{ 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
{ 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
{ 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
{ 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
{ 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
{ 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
{ 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
{ 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
{ 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
{ 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
{ 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
{ 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
{ 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
{ 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
{ 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
{ 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
{ 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
{ 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
{ 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
{ 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
{ 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
};
static const u32 treble_table[41][5] = {
{ 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
{ 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
{ 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
{ 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
{ 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
{ 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
{ 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
{ 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
{ 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
{ 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
{ 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
{ 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
{ 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
{ 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
{ 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
{ 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
{ 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
{ 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
{ 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
{ 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
{ 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
{ 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
{ 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
{ 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
{ 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
{ 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
{ 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
{ 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
{ 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
{ 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
{ 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
{ 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
{ 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
{ 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
{ 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
{ 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
{ 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
{ 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
{ 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
{ 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
};
static void set_bass(struct emu10k1_card *card, int l, int r)
{
int i;
l = (l * 40 + 50) / 100;
r = (r * 40 + 50) / 100;
for (i = 0; i < 5; i++)
sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + card->mgr.ctrl_gpr[SOUND_MIXER_BASS][0] + i, 0, bass_table[l][i]);
}
static void set_treble(struct emu10k1_card *card, int l, int r)
{
int i;
l = (l * 40 + 50) / 100;
r = (r * 40 + 50) / 100;
for (i = 0; i < 5; i++)
sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + card->mgr.ctrl_gpr[SOUND_MIXER_TREBLE][0] + i , 0, treble_table[l][i]);
}
const char volume_params[SOUND_MIXER_NRDEVICES]= {
/* Used by the ac97 driver */
[SOUND_MIXER_VOLUME] = VOL_6BIT,
[SOUND_MIXER_BASS] = VOL_4BIT,
[SOUND_MIXER_TREBLE] = VOL_4BIT,
[SOUND_MIXER_PCM] = VOL_5BIT,
[SOUND_MIXER_SPEAKER] = VOL_4BIT,
[SOUND_MIXER_LINE] = VOL_5BIT,
[SOUND_MIXER_MIC] = VOL_5BIT,
[SOUND_MIXER_CD] = VOL_5BIT,
[SOUND_MIXER_ALTPCM] = VOL_6BIT,
[SOUND_MIXER_IGAIN] = VOL_4BIT,
[SOUND_MIXER_LINE1] = VOL_5BIT,
[SOUND_MIXER_PHONEIN] = VOL_5BIT,
[SOUND_MIXER_PHONEOUT] = VOL_6BIT,
[SOUND_MIXER_VIDEO] = VOL_5BIT,
/* Not used by the ac97 driver */
[SOUND_MIXER_SYNTH] = VOL_5BIT,
[SOUND_MIXER_IMIX] = VOL_5BIT,
[SOUND_MIXER_RECLEV] = VOL_5BIT,
[SOUND_MIXER_OGAIN] = VOL_5BIT,
[SOUND_MIXER_LINE2] = VOL_5BIT,
[SOUND_MIXER_LINE3] = VOL_5BIT,
[SOUND_MIXER_DIGITAL1] = VOL_5BIT,
[SOUND_MIXER_DIGITAL2] = VOL_5BIT,
[SOUND_MIXER_DIGITAL3] = VOL_5BIT,
[SOUND_MIXER_RADIO] = VOL_5BIT,
[SOUND_MIXER_MONITOR] = VOL_5BIT
};
/* Mixer file operations */
static int emu10k1_private_mixer(struct emu10k1_card *card, unsigned int cmd, unsigned long arg)
{
struct mixer_private_ioctl *ctl;
struct dsp_patch *patch;
u32 size, page;
int addr, size_reg, i, ret;
unsigned int id, ch;
void __user *argp = (void __user *)arg;
switch (cmd) {
case SOUND_MIXER_PRIVATE3:
ctl = (struct mixer_private_ioctl *) kmalloc(sizeof(struct mixer_private_ioctl), GFP_KERNEL);
if (ctl == NULL)
return -ENOMEM;
if (copy_from_user(ctl, argp, sizeof(struct mixer_private_ioctl))) {
kfree(ctl);
return -EFAULT;
}
ret = 0;
switch (ctl->cmd) {
#ifdef DBGEMU
case CMD_WRITEFN0:
emu10k1_writefn0_2(card, ctl->val[0], ctl->val[1], ctl->val[2]);
break;
#endif
case CMD_WRITEPTR:
#ifdef DBGEMU
if (ctl->val[1] >= 0x40 || ctl->val[0] >= 0x1000) {
#else
if (ctl->val[1] >= 0x40 || ctl->val[0] >= 0x1000 || ((ctl->val[0] < 0x100 ) &&
//Any register allowed raw access goes here:
(ctl->val[0] != A_SPDIF_SAMPLERATE) && (ctl->val[0] != A_DBG)
)
) {
#endif
ret = -EINVAL;
break;
}
sblive_writeptr(card, ctl->val[0], ctl->val[1], ctl->val[2]);
break;
case CMD_READFN0:
ctl->val[2] = emu10k1_readfn0(card, ctl->val[0]);
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_READPTR:
if (ctl->val[1] >= 0x40 || (ctl->val[0] & 0x7ff) > 0xff) {
ret = -EINVAL;
break;
}
if ((ctl->val[0] & 0x7ff) > 0x3f)
ctl->val[1] = 0x00;
ctl->val[2] = sblive_readptr(card, ctl->val[0], ctl->val[1]);
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_SETRECSRC:
switch (ctl->val[0]) {
case WAVERECORD_AC97:
if (card->is_aps) {
ret = -EINVAL;
break;
}
card->wavein.recsrc = WAVERECORD_AC97;
break;
case WAVERECORD_MIC:
card->wavein.recsrc = WAVERECORD_MIC;
break;
case WAVERECORD_FX:
card->wavein.recsrc = WAVERECORD_FX;
card->wavein.fxwc = ctl->val[1] & 0xffff;
if (!card->wavein.fxwc)
ret = -EINVAL;
break;
default:
ret = -EINVAL;
break;
}
break;
case CMD_GETRECSRC:
ctl->val[0] = card->wavein.recsrc;
ctl->val[1] = card->wavein.fxwc;
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_GETVOICEPARAM:
ctl->val[0] = card->waveout.send_routing[0];
ctl->val[1] = card->waveout.send_dcba[0];
ctl->val[2] = card->waveout.send_routing[1];
ctl->val[3] = card->waveout.send_dcba[1];
ctl->val[4] = card->waveout.send_routing[2];
ctl->val[5] = card->waveout.send_dcba[2];
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_SETVOICEPARAM:
card->waveout.send_routing[0] = ctl->val[0];
card->waveout.send_dcba[0] = ctl->val[1];
card->waveout.send_routing[1] = ctl->val[2];
card->waveout.send_dcba[1] = ctl->val[3];
card->waveout.send_routing[2] = ctl->val[4];
card->waveout.send_dcba[2] = ctl->val[5];
break;
case CMD_SETMCH_FX:
card->mchannel_fx = ctl->val[0] & 0x000f;
break;
case CMD_GETPATCH:
if (ctl->val[0] == 0) {
if (copy_to_user(argp, &card->mgr.rpatch, sizeof(struct dsp_rpatch)))
ret = -EFAULT;
} else {
if ((ctl->val[0] - 1) / PATCHES_PER_PAGE >= card->mgr.current_pages) {
ret = -EINVAL;
break;
}
if (copy_to_user(argp, PATCH(&card->mgr, ctl->val[0] - 1), sizeof(struct dsp_patch)))
ret = -EFAULT;
}
break;
case CMD_GETGPR:
id = ctl->val[0];
if (id > NUM_GPRS) {
ret = -EINVAL;
break;
}
if (copy_to_user(argp, &card->mgr.gpr[id], sizeof(struct dsp_gpr)))
ret = -EFAULT;
break;
case CMD_GETCTLGPR:
addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, &((char *) ctl->val)[PATCH_NAME_SIZE]);
ctl->val[0] = sblive_readptr(card, addr, 0);
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_SETPATCH:
if (ctl->val[0] == 0)
memcpy(&card->mgr.rpatch, &ctl->val[1], sizeof(struct dsp_rpatch));
else {
page = (ctl->val[0] - 1) / PATCHES_PER_PAGE;
if (page > MAX_PATCHES_PAGES) {
ret = -EINVAL;
break;
}
if (page >= card->mgr.current_pages) {
for (i = card->mgr.current_pages; i < page + 1; i++) {
card->mgr.patch[i] = (void *)__get_free_page(GFP_KERNEL);
if(card->mgr.patch[i] == NULL) {
card->mgr.current_pages = i;
ret = -ENOMEM;
break;
}
memset(card->mgr.patch[i], 0, PAGE_SIZE);
}
card->mgr.current_pages = page + 1;
}
patch = PATCH(&card->mgr, ctl->val[0] - 1);
memcpy(patch, &ctl->val[1], sizeof(struct dsp_patch));
if (patch->code_size == 0) {
for(i = page + 1; i < card->mgr.current_pages; i++)
free_page((unsigned long) card->mgr.patch[i]);
card->mgr.current_pages = page + 1;
}
}
break;
case CMD_SETGPR:
if (ctl->val[0] > NUM_GPRS) {
ret = -EINVAL;
break;
}
memcpy(&card->mgr.gpr[ctl->val[0]], &ctl->val[1], sizeof(struct dsp_gpr));
break;
case CMD_SETCTLGPR:
addr = emu10k1_find_control_gpr(&card->mgr, (char *) ctl->val, (char *) ctl->val + PATCH_NAME_SIZE);
emu10k1_set_control_gpr(card, addr, *((s32 *)((char *) ctl->val + 2 * PATCH_NAME_SIZE)), 0);
break;
case CMD_SETGPOUT:
if ( ((ctl->val[0] > 2) && (!card->is_audigy))
|| (ctl->val[0] > 15) || ctl->val[1] > 1) {
ret= -EINVAL;
break;
}
if (card->is_audigy)
emu10k1_writefn0(card, (1 << 24) | ((ctl->val[0]) << 16) | A_IOCFG, ctl->val[1]);
else
emu10k1_writefn0(card, (1 << 24) | (((ctl->val[0]) + 10) << 16) | HCFG, ctl->val[1]);
break;
case CMD_GETGPR2OSS:
id = ctl->val[0];
ch = ctl->val[1];
if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) {
ret = -EINVAL;
break;
}
ctl->val[2] = card->mgr.ctrl_gpr[id][ch];
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_SETGPR2OSS:
id = ctl->val[0];
/* 0 == left, 1 == right */
ch = ctl->val[1];
addr = ctl->val[2];
if (id >= SOUND_MIXER_NRDEVICES || ch >= 2) {
ret = -EINVAL;
break;
}
card->mgr.ctrl_gpr[id][ch] = addr;
if (card->is_aps)
break;
if (addr >= 0) {
unsigned int state = card->ac97->mixer_state[id];
if (ch == 1) {
state >>= 8;
card->ac97->stereo_mixers |= (1 << id);
}
card->ac97->supported_mixers |= (1 << id);
if (id == SOUND_MIXER_TREBLE) {
set_treble(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff);
} else if (id == SOUND_MIXER_BASS) {
set_bass(card, card->ac97->mixer_state[id] & 0xff, (card->ac97->mixer_state[id] >> 8) & 0xff);
} else
emu10k1_set_volume_gpr(card, addr, state & 0xff,
volume_params[id]);
} else {
card->ac97->stereo_mixers &= ~(1 << id);
card->ac97->stereo_mixers |= card->ac97_stereo_mixers;
if (ch == 0) {
card->ac97->supported_mixers &= ~(1 << id);
card->ac97->supported_mixers |= card->ac97_supported_mixers;
}
}
break;
case CMD_SETPASSTHROUGH:
card->pt.selected = ctl->val[0] ? 1 : 0;
if (card->pt.state != PT_STATE_INACTIVE)
break;
card->pt.spcs_to_use = ctl->val[0] & 0x07;
break;
case CMD_PRIVATE3_VERSION:
ctl->val[0] = PRIVATE3_VERSION; //private3 version
ctl->val[1] = MAJOR_VER; //major driver version
ctl->val[2] = MINOR_VER; //minor driver version
ctl->val[3] = card->is_audigy; //1=card is audigy
if (card->is_audigy)
ctl->val[4]=emu10k1_readfn0(card, 0x18);
if (copy_to_user(argp, ctl, sizeof(struct mixer_private_ioctl)))
ret = -EFAULT;
break;
case CMD_AC97_BOOST:
if (ctl->val[0])
emu10k1_ac97_write(card->ac97, 0x18, 0x0);
else
emu10k1_ac97_write(card->ac97, 0x18, 0x0808);
break;
default:
ret = -EINVAL;
break;
}
kfree(ctl);
return ret;
break;
case SOUND_MIXER_PRIVATE4:
if (copy_from_user(&size, argp, sizeof(size)))
return -EFAULT;
DPD(2, "External tram size %#x\n", size);
if (size > 0x1fffff)
return -EINVAL;
size_reg = 0;
if (size != 0) {
size = (size - 1) >> 14;
while (size) {
size >>= 1;
size_reg++;
}
size = 0x4000 << size_reg;
}
DPD(2, "External tram size %#x %#x\n", size, size_reg);
if (size != card->tankmem.size) {
if (card->tankmem.size > 0) {
emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 1);
sblive_writeptr_tag(card, 0, TCB, 0, TCBS, 0, TAGLIST_END);
pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle);
card->tankmem.size = 0;
}
if (size != 0) {
card->tankmem.addr = pci_alloc_consistent(card->pci_dev, size, &card->tankmem.dma_handle);
if (card->tankmem.addr == NULL)
return -ENOMEM;
card->tankmem.size = size;
sblive_writeptr_tag(card, 0, TCB, (u32) card->tankmem.dma_handle, TCBS,(u32) size_reg, TAGLIST_END);
emu10k1_writefn0(card, HCFG_LOCKTANKCACHE, 0);
}
}
return 0;
break;
default:
break;
}
return -EINVAL;
}
static int emu10k1_dsp_mixer(struct emu10k1_card *card, unsigned int oss_mixer, unsigned long arg)
{
unsigned int left, right;
int val;
int scale;
card->ac97->modcnt++;
if (get_user(val, (int __user *)arg))
return -EFAULT;
/* cleanse input a little */
right = ((val >> 8) & 0xff);
left = (val & 0xff);
if (right > 100) right = 100;
if (left > 100) left = 100;
card->ac97->mixer_state[oss_mixer] = (right << 8) | left;
if (oss_mixer == SOUND_MIXER_TREBLE) {
set_treble(card, left, right);
return 0;
} if (oss_mixer == SOUND_MIXER_BASS) {
set_bass(card, left, right);
return 0;
}
if (oss_mixer == SOUND_MIXER_VOLUME)
scale = 1 << card->ac97->bit_resolution;
else
scale = volume_params[oss_mixer];
emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][0], left, scale);
emu10k1_set_volume_gpr(card, card->mgr.ctrl_gpr[oss_mixer][1], right, scale);
if (card->ac97_supported_mixers & (1 << oss_mixer))
card->ac97->write_mixer(card->ac97, oss_mixer, left, right);
return 0;
}
static int emu10k1_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
struct emu10k1_card *card = file->private_data;
unsigned int oss_mixer = _IOC_NR(cmd);
ret = -EINVAL;
if (!card->is_aps) {
if (cmd == SOUND_MIXER_INFO) {
mixer_info info;
strlcpy(info.id, card->ac97->name, sizeof(info.id));
if (card->is_audigy)
strlcpy(info.name, "Audigy - Emu10k1", sizeof(info.name));
else
strlcpy(info.name, "Creative SBLive - Emu10k1", sizeof(info.name));
info.modify_counter = card->ac97->modcnt;
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
if ((_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES)
ret = emu10k1_dsp_mixer(card, oss_mixer, arg);
else
ret = card->ac97->mixer_ioctl(card->ac97, cmd, arg);
}
if (ret < 0)
ret = emu10k1_private_mixer(card, cmd, arg);
return ret;
}
static int emu10k1_mixer_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct emu10k1_card *card = NULL;
struct list_head *entry;
DPF(4, "emu10k1_mixer_open()\n");
list_for_each(entry, &emu10k1_devs) {
card = list_entry(entry, struct emu10k1_card, list);
if (card->ac97->dev_mixer == minor)
goto match;
}
return -ENODEV;
match:
file->private_data = card;
return 0;
}
static int emu10k1_mixer_release(struct inode *inode, struct file *file)
{
DPF(4, "emu10k1_mixer_release()\n");
return 0;
}
struct file_operations emu10k1_mixer_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = emu10k1_mixer_ioctl,
.open = emu10k1_mixer_open,
.release = emu10k1_mixer_release,
};

View File

@@ -0,0 +1,236 @@
/*
**********************************************************************
* passthrough.c -- Emu10k1 digital passthrough
* Copyright (C) 2001 Juha Yrj<72>l<EFBFBD> <jyrjola@cc.hut.fi>
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* May 15, 2001 Juha Yrj<72>l<EFBFBD> base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include "hwaccess.h"
#include "cardwo.h"
#include "cardwi.h"
#include "recmgr.h"
#include "irqmgr.h"
#include "audio.h"
#include "8010.h"
static void pt_putsamples(struct pt_data *pt, u16 *ptr, u16 left, u16 right)
{
unsigned int idx;
ptr[pt->copyptr] = left;
idx = pt->copyptr + PT_SAMPLES/2;
idx %= PT_SAMPLES;
ptr[idx] = right;
}
static inline int pt_can_write(struct pt_data *pt)
{
return pt->blocks_copied < pt->blocks_played + 8;
}
static int pt_wait_for_write(struct emu10k1_wavedevice *wavedev, int nonblock)
{
struct emu10k1_card *card = wavedev->card;
struct pt_data *pt = &card->pt;
if (nonblock && !pt_can_write(pt))
return -EAGAIN;
while (!pt_can_write(pt) && pt->state != PT_STATE_INACTIVE) {
interruptible_sleep_on(&pt->wait);
if (signal_pending(current))
return -ERESTARTSYS;
}
if (pt->state == PT_STATE_INACTIVE)
return -EAGAIN;
return 0;
}
static int pt_putblock(struct emu10k1_wavedevice *wave_dev, u16 *block, int nonblock)
{
struct woinst *woinst = wave_dev->woinst;
struct emu10k1_card *card = wave_dev->card;
struct pt_data *pt = &card->pt;
u16 *ptr = (u16 *) card->tankmem.addr;
int i = 0, r;
unsigned long flags;
r = pt_wait_for_write(wave_dev, nonblock);
if (r < 0)
return r;
spin_lock_irqsave(&card->pt.lock, flags);
while (i < PT_BLOCKSAMPLES) {
pt_putsamples(pt, ptr, block[2*i], block[2*i+1]);
if (pt->copyptr == 0)
pt->copyptr = PT_SAMPLES;
pt->copyptr--;
i++;
}
woinst->total_copied += PT_BLOCKSIZE;
pt->blocks_copied++;
if (pt->blocks_copied >= 4 && pt->state != PT_STATE_PLAYING) {
DPF(2, "activating digital pass-through playback\n");
sblive_writeptr(card, GPR_BASE + pt->enable_gpr, 0, 1);
pt->state = PT_STATE_PLAYING;
}
spin_unlock_irqrestore(&card->pt.lock, flags);
return 0;
}
int emu10k1_pt_setup(struct emu10k1_wavedevice *wave_dev)
{
u32 bits;
struct emu10k1_card *card = wave_dev->card;
struct pt_data *pt = &card->pt;
int i;
for (i = 0; i < 3; i++) {
pt->old_spcs[i] = sblive_readptr(card, SPCS0 + i, 0);
if (pt->spcs_to_use & (1 << i)) {
DPD(2, "using S/PDIF port %d\n", i);
bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
if (pt->ac3data)
bits |= SPCS_NOTAUDIODATA;
sblive_writeptr(card, SPCS0 + i, 0, bits);
}
}
return 0;
}
ssize_t emu10k1_pt_write(struct file *file, const char __user *buffer, size_t count)
{
struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data;
struct emu10k1_card *card = wave_dev->card;
struct pt_data *pt = &card->pt;
int nonblock, i, r, blocks, blocks_copied, bytes_copied = 0;
DPD(3, "emu10k1_pt_write(): %d bytes\n", count);
nonblock = file->f_flags & O_NONBLOCK;
if (card->tankmem.size < PT_SAMPLES*2)
return -EFAULT;
if (pt->state == PT_STATE_INACTIVE) {
DPF(2, "bufptr init\n");
pt->playptr = PT_SAMPLES-1;
pt->copyptr = PT_INITPTR;
pt->blocks_played = pt->blocks_copied = 0;
memset(card->tankmem.addr, 0, card->tankmem.size);
pt->state = PT_STATE_ACTIVATED;
pt->buf = kmalloc(PT_BLOCKSIZE, GFP_KERNEL);
pt->prepend_size = 0;
if (pt->buf == NULL)
return -ENOMEM;
emu10k1_pt_setup(wave_dev);
}
if (pt->prepend_size) {
int needed = PT_BLOCKSIZE - pt->prepend_size;
DPD(3, "prepend size %d, prepending %d bytes\n", pt->prepend_size, needed);
if (count < needed) {
copy_from_user(pt->buf + pt->prepend_size, buffer, count);
pt->prepend_size += count;
DPD(3, "prepend size now %d\n", pt->prepend_size);
return count;
}
copy_from_user(pt->buf + pt->prepend_size, buffer, needed);
r = pt_putblock(wave_dev, (u16 *) pt->buf, nonblock);
if (r)
return r;
bytes_copied += needed;
pt->prepend_size = 0;
}
blocks = (count-bytes_copied)/PT_BLOCKSIZE;
blocks_copied = 0;
while (blocks > 0) {
u16 __user *bufptr = (u16 __user *) buffer + (bytes_copied/2);
copy_from_user(pt->buf, bufptr, PT_BLOCKSIZE);
r = pt_putblock(wave_dev, (u16 *)pt->buf, nonblock);
if (r) {
if (bytes_copied)
return bytes_copied;
else
return r;
}
bytes_copied += PT_BLOCKSIZE;
blocks--;
blocks_copied++;
}
i = count - bytes_copied;
if (i) {
pt->prepend_size = i;
copy_from_user(pt->buf, buffer + bytes_copied, i);
bytes_copied += i;
DPD(3, "filling prepend buffer with %d bytes", i);
}
return bytes_copied;
}
void emu10k1_pt_stop(struct emu10k1_card *card)
{
struct pt_data *pt = &card->pt;
int i;
if (pt->state != PT_STATE_INACTIVE) {
DPF(2, "digital pass-through stopped\n");
sblive_writeptr(card, (card->is_audigy ? A_GPR_BASE : GPR_BASE) + pt->enable_gpr, 0, 0);
for (i = 0; i < 3; i++) {
if (pt->spcs_to_use & (1 << i))
sblive_writeptr(card, SPCS0 + i, 0, pt->old_spcs[i]);
}
pt->state = PT_STATE_INACTIVE;
if(pt->buf)
kfree(pt->buf);
}
}
void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev)
{
struct woinst *woinst = wave_dev->woinst;
struct pt_data *pt = &wave_dev->card->pt;
u32 pos;
if (pt->state == PT_STATE_PLAYING && pt->pos_gpr >= 0) {
pos = sblive_readptr(wave_dev->card, GPR_BASE + pt->pos_gpr, 0);
if (pos > PT_BLOCKSAMPLES)
pos = PT_BLOCKSAMPLES;
pos = 4 * (PT_BLOCKSAMPLES - pos);
} else
pos = 0;
woinst->total_played = pt->blocks_played * woinst->buffer.fragment_size + pos;
woinst->buffer.hw_pos = pos;
}

View File

@@ -0,0 +1,99 @@
/*
**********************************************************************
* passthrough.h -- Emu10k1 digital passthrough header file
* Copyright (C) 2001 Juha Yrj<72>l<EFBFBD> <jyrjola@cc.hut.fi>
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* May 15, 2001 Juha Yrj<72>l<EFBFBD> base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _PASSTHROUGH_H
#define _PASSTHROUGH_H
#include "audio.h"
/* number of 16-bit stereo samples in XTRAM buffer */
#define PT_SAMPLES 0x8000
#define PT_BLOCKSAMPLES 0x400
#define PT_BLOCKSIZE (PT_BLOCKSAMPLES*4)
#define PT_BLOCKSIZE_LOG2 12
#define PT_BLOCKCOUNT (PT_SAMPLES/PT_BLOCKSAMPLES)
#define PT_INITPTR (PT_SAMPLES/2-1)
#define PT_STATE_INACTIVE 0
#define PT_STATE_ACTIVATED 1
#define PT_STATE_PLAYING 2
/* passthrough struct */
struct pt_data
{
u8 selected, state, spcs_to_use;
int intr_gpr, enable_gpr, pos_gpr;
u32 blocks_played, blocks_copied, old_spcs[3];
u32 playptr, copyptr;
u32 prepend_size;
u8 *buf;
u8 ac3data;
char *patch_name, *intr_gpr_name, *enable_gpr_name, *pos_gpr_name;
wait_queue_head_t wait;
spinlock_t lock;
};
/*
Passthrough can be done in two methods:
Method 1 : tram
In original emu10k1, we couldn't bypass the sample rate converters. Even at 48kHz
(the internal sample rate of the emu10k1) the samples would get messed up.
To over come this, samples are copied into the tram and a special dsp patch copies
the samples out and generates interrupts when a block has finnished playing.
Method 2 : Interpolator bypass
Creative fixed the sample rate convert problem in emu10k1 rev 7 and higher
(including the emu10k2 (audigy)). This allows us to use the regular, and much simpler
playback method.
In both methods, dsp code is used to mux audio and passthrough. This ensures that the spdif
doesn't receive audio and pasthrough data at the same time. The spdif flag SPCS_NOTAUDIODATA
is set to tell
*/
// emu10k1 revs greater than or equal to 7 can use method2
#define USE_PT_METHOD2 (card->is_audigy)
#define USE_PT_METHOD1 !USE_PT_METHOD2
ssize_t emu10k1_pt_write(struct file *file, const char __user *buf, size_t count);
int emu10k1_pt_setup(struct emu10k1_wavedevice *wave_dev);
void emu10k1_pt_stop(struct emu10k1_card *card);
void emu10k1_pt_waveout_update(struct emu10k1_wavedevice *wave_dev);
#endif /* _PASSTHROUGH_H */

147
sound/oss/emu10k1/recmgr.c Normal file
View File

@@ -0,0 +1,147 @@
/*
**********************************************************************
* recmgr.c -- Recording manager for emu10k1 driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#include <asm/delay.h>
#include "8010.h"
#include "recmgr.h"
void emu10k1_reset_record(struct emu10k1_card *card, struct wavein_buffer *buffer)
{
DPF(2, "emu10k1_reset_record()\n");
sblive_writeptr(card, buffer->sizereg, 0, ADCBS_BUFSIZE_NONE);
sblive_writeptr(card, buffer->sizereg, 0, buffer->sizeregval);
while (sblive_readptr(card, buffer->idxreg, 0))
udelay(5);
}
void emu10k1_start_record(struct emu10k1_card *card, struct wavein_buffer *buffer)
{
DPF(2, "emu10k1_start_record()\n");
if (buffer->adcctl)
sblive_writeptr(card, ADCCR, 0, buffer->adcctl);
}
void emu10k1_stop_record(struct emu10k1_card *card, struct wavein_buffer *buffer)
{
DPF(2, "emu10k1_stop_record()\n");
/* Disable record transfer */
if (buffer->adcctl)
sblive_writeptr(card, ADCCR, 0, 0);
}
void emu10k1_set_record_src(struct emu10k1_card *card, struct wiinst *wiinst)
{
struct wavein_buffer *buffer = &wiinst->buffer;
DPF(2, "emu10k1_set_record_src()\n");
switch (wiinst->recsrc) {
case WAVERECORD_AC97:
DPF(2, "recording source: AC97\n");
buffer->sizereg = ADCBS;
buffer->addrreg = ADCBA;
buffer->idxreg = card->is_audigy ? A_ADCIDX_IDX : ADCIDX_IDX;
switch (wiinst->format.samplingrate) {
case 0xBB80:
buffer->adcctl = ADCCR_SAMPLERATE_48;
break;
case 0xAC44:
buffer->adcctl = ADCCR_SAMPLERATE_44;
break;
case 0x7D00:
buffer->adcctl = ADCCR_SAMPLERATE_32;
break;
case 0x5DC0:
buffer->adcctl = ADCCR_SAMPLERATE_24;
break;
case 0x5622:
buffer->adcctl = ADCCR_SAMPLERATE_22;
break;
case 0x3E80:
buffer->adcctl = ADCCR_SAMPLERATE_16;
break;
// FIXME: audigy supports 12kHz recording
/*
case ????:
buffer->adcctl = A_ADCCR_SAMPLERATE_12;
break;
*/
case 0x2B11:
buffer->adcctl = card->is_audigy ? A_ADCCR_SAMPLERATE_11 : ADCCR_SAMPLERATE_11;
break;
case 0x1F40:
buffer->adcctl = card->is_audigy ? A_ADCCR_SAMPLERATE_8 : ADCCR_SAMPLERATE_8;
break;
default:
BUG();
break;
}
buffer->adcctl |= card->is_audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
if (wiinst->format.channels == 2)
buffer->adcctl |= card->is_audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
break;
case WAVERECORD_MIC:
DPF(2, "recording source: MIC\n");
buffer->sizereg = MICBS;
buffer->addrreg = MICBA;
buffer->idxreg = MICIDX_IDX;
buffer->adcctl = 0;
break;
case WAVERECORD_FX:
DPF(2, "recording source: FX\n");
buffer->sizereg = FXBS;
buffer->addrreg = FXBA;
buffer->idxreg = FXIDX_IDX;
buffer->adcctl = 0;
sblive_writeptr(card, FXWC, 0, wiinst->fxwc);
break;
default:
BUG();
break;
}
DPD(2, "bus addx: %#lx\n", (unsigned long) buffer->dma_handle);
sblive_writeptr(card, buffer->addrreg, 0, (u32)buffer->dma_handle);
}

View File

@@ -0,0 +1,48 @@
/*
**********************************************************************
* recmgr.h
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*/
#ifndef _RECORDMGR_H
#define _RECORDMGR_H
#include "hwaccess.h"
#include "cardwi.h"
/* Recording resources */
#define WAVERECORD_AC97 0x01
#define WAVERECORD_MIC 0x02
#define WAVERECORD_FX 0x03
void emu10k1_reset_record(struct emu10k1_card *card, struct wavein_buffer *buffer);
void emu10k1_start_record(struct emu10k1_card *, struct wavein_buffer *);
void emu10k1_stop_record(struct emu10k1_card *, struct wavein_buffer *);
void emu10k1_set_record_src(struct emu10k1_card *, struct wiinst *wiinst);
#endif /* _RECORDMGR_H */

Some files were not shown because too many files have changed in this diff Show More