Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa

* 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa: (102 commits)
  [ALSA] version 1.0.14
  [ALSA] remove duplicate Logitech Quickcam USB ID in usbquirks.h
  [ALSA] hda-codec - Fix input with STAC92xx
  [ALSA] hda-intel: support for iMac 24'' released on 09/2006
  [ALSA] hda-codec - Add quirk for Asus P5LD2
  [ALSA] snd-ca0106: Add support for X-Fi Extreme Audio.
  [ALSA] snd-emu10k1:Enable E-Mu 1616m notebook firmware loading.
  [ALSA] snd-emu10k1: Initial support for E-Mu 1616 and 1616m.
  [ALSA] cs46xx - Fix PM resume
  [ALSA] hda: Enable SPDIF in/out on some stac9205 boards
  [ALSA] timer: check for incorrect device state in non-debug compiles, too
  [ALSA] snd-aoa-codec-onyx: fix typo
  [ALSA] hda-codec - Add quirks for HP dx2200/dx2250
  [ALSA] hda-codec - Rename HP model-specific quirks
  [ALSA] hda-codec - Add quirk for HP Samba
  [ALSA] hda-codec - Add LG LW20 line-in capture source
  [ALSA] usb-audio - Fix AC3 with M-Audio Audiophile USB
  [ALSA] hda: stac9202 mixer fix
  [ALSA] Make s3c24xx_i2s_set_clkdiv() change the correct bits
  [ALSA] hda-codec - Add LG LW20 si3054 modem id
  ...
This commit is contained in:
Linus Torvalds 2007-07-20 08:52:06 -07:00
commit 7f46e6ca01
89 changed files with 8425 additions and 398 deletions

10
CREDITS
View File

@ -2212,13 +2212,13 @@ S: 2300 Copenhagen S
S: Denmark S: Denmark
N: Claudio S. Matsuoka N: Claudio S. Matsuoka
E: claudio@conectiva.com E: cmatsuoka@gmail.com
E: claudio@helllabs.org E: claudio@mandriva.com
W: http://helllabs.org/~claudio W: http://helllabs.org/~claudio
D: V4L, OV511 driver hacks D: V4L, OV511 and HDA-codec hacks
S: Conectiva S.A. S: Conectiva S.A.
S: R. Tocantins 89 S: Souza Naves 1250
S: 80050-430 Curitiba PR S: 80050-040 Curitiba PR
S: Brazil S: Brazil
N: Heinz Mauelshagen N: Heinz Mauelshagen

View File

@ -467,7 +467,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
above explicitly. above explicitly.
The power-management is supported. The power-management is supported.
Module snd-cs5530
_________________
Module for Cyrix/NatSemi Geode 5530 chip.
Module snd-cs5535audio Module snd-cs5535audio
---------------------- ----------------------
@ -759,6 +764,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
model - force the model name model - force the model name
position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size) position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
single_cmd - Use single immediate commands to communicate with single_cmd - Use single immediate commands to communicate with
codecs (for debugging only) codecs (for debugging only)
enable_msi - Enable Message Signaled Interrupt (MSI) (default = off) enable_msi - Enable Message Signaled Interrupt (MSI) (default = off)
@ -803,6 +809,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
hp-3013 HP machines (3013-variant) hp-3013 HP machines (3013-variant)
fujitsu Fujitsu S7020 fujitsu Fujitsu S7020
acer Acer TravelMate acer Acer TravelMate
will Will laptops (PB V7900)
replacer Replacer 672V
basic fixed pin assignment (old default model) basic fixed pin assignment (old default model)
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
@ -811,16 +819,31 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
hp-bpc HP xw4400/6400/8400/9400 laptops hp-bpc HP xw4400/6400/8400/9400 laptops
hp-bpc-d7000 HP BPC D7000 hp-bpc-d7000 HP BPC D7000
benq Benq ED8 benq Benq ED8
benq-t31 Benq T31
hippo Hippo (ATI) with jack detection, Sony UX-90s hippo Hippo (ATI) with jack detection, Sony UX-90s
hippo_1 Hippo (Benq) with jack detection hippo_1 Hippo (Benq) with jack detection
sony-assamd Sony ASSAMD
basic fixed pin assignment w/o SPDIF basic fixed pin assignment w/o SPDIF
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
ALC268
3stack 3-stack model
auto auto-config reading BIOS (default)
ALC662
3stack-dig 3-stack (2-channel) with SPDIF
3stack-6ch 3-stack (6-channel)
3stack-6ch-dig 3-stack (6-channel) with SPDIF
6stack-dig 6-stack with SPDIF
lenovo-101e Lenovo laptop
auto auto-config reading BIOS (default)
ALC882/885 ALC882/885
3stack-dig 3-jack with SPDIF I/O 3stack-dig 3-jack with SPDIF I/O
6stack-dig 6-jack digital with SPDIF I/O 6stack-dig 6-jack digital with SPDIF I/O
arima Arima W820Di1 arima Arima W820Di1
macpro MacPro support macpro MacPro support
imac24 iMac 24'' with jack detection
w2jc ASUS W2JC w2jc ASUS W2JC
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
@ -832,9 +855,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
6stack-dig-demo 6-jack digital for Intel demo board 6stack-dig-demo 6-jack digital for Intel demo board
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc) acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
medion Medion Laptops medion Medion Laptops
medion-md2 Medion MD2
targa-dig Targa/MSI targa-dig Targa/MSI
targa-2ch-dig Targs/MSI with 2-channel targa-2ch-dig Targs/MSI with 2-channel
laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE) laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
lenovo-101e Lenovo 101E
lenovo-nb0763 Lenovo NB0763
lenovo-ms7195-dig Lenovo MS7195
6stack-hp HP machines with 6stack (Nettle boards)
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
ALC861/660 ALC861/660
@ -853,7 +882,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
3stack-dig 3-jack with SPDIF OUT 3stack-dig 3-jack with SPDIF OUT
6stack-dig 6-jack with SPDIF OUT 6stack-dig 6-jack with SPDIF OUT
3stack-660 3-jack (for ALC660VD) 3stack-660 3-jack (for ALC660VD)
3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
lenovo Lenovo 3000 C200 lenovo Lenovo 3000 C200
dallas Dallas laptops
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
CMI9880 CMI9880
@ -864,12 +895,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
allout 5-jack in back, 2-jack in front, SPDIF out allout 5-jack in back, 2-jack in front, SPDIF out
auto auto-config reading BIOS (default) auto auto-config reading BIOS (default)
AD1882
3stack 3-stack mode (default)
6stack 6-stack mode
AD1884
N/A
AD1981 AD1981
basic 3-jack (default) basic 3-jack (default)
hp HP nx6320 hp HP nx6320
thinkpad Lenovo Thinkpad T60/X60/Z60 thinkpad Lenovo Thinkpad T60/X60/Z60
toshiba Toshiba U205 toshiba Toshiba U205
AD1983
N/A
AD1984
basic default configuration
thinkpad Lenovo Thinkpad T61/X61
AD1986A AD1986A
6stack 6-jack, separate surrounds (default) 6stack 6-jack, separate surrounds (default)
3stack 3-stack, shared surrounds 3stack 3-stack, shared surrounds
@ -907,11 +952,18 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
ref Reference board ref Reference board
3stack D945 3stack 3stack D945 3stack
5stack D945 5stack + SPDIF 5stack D945 5stack + SPDIF
macmini Intel Mac Mini dell Dell XPS M1210
macbook Intel Mac Book intel-mac-v1 Intel Mac Type 1
macbook-pro-v1 Intel Mac Book Pro 1st generation intel-mac-v2 Intel Mac Type 2
macbook-pro Intel Mac Book Pro 2nd generation intel-mac-v3 Intel Mac Type 3
imac-intel Intel iMac intel-mac-v4 Intel Mac Type 4
intel-mac-v5 Intel Mac Type 5
macmini Intel Mac Mini (equivalent with type 3)
macbook Intel Mac Book (eq. type 5)
macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
imac-intel Intel iMac (eq. type 2)
imac-intel-20 Intel iMac (newer version) (eq. type 3)
STAC9202/9250/9251 STAC9202/9250/9251
ref Reference board, base config ref Reference board, base config
@ -956,6 +1008,17 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
from the irq. Remember this is a last resort, and should be from the irq. Remember this is a last resort, and should be
avoided as much as possible... avoided as much as possible...
MORE NOTES ON "azx_get_response timeout" PROBLEMS:
On some hardwares, you may need to add a proper probe_mask option
to avoid the "azx_get_response timeout" problem above, instead.
This occurs when the access to non-existing or non-working codec slot
(likely a modem one) causes a stall of the communication via HD-audio
bus. You can see which codec slots are probed by enabling
CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
proc files. Then limit the slots to probe by probe_mask option.
For example, probe_mask=1 means to probe only the first slot, and
probe_mask=4 means only the third slot.
The power-management is supported. The power-management is supported.
Module snd-hdsp Module snd-hdsp

View File

@ -1,4 +1,4 @@
Guide to using M-Audio Audiophile USB with ALSA and Jack v1.3 Guide to using M-Audio Audiophile USB with ALSA and Jack v1.5
======================================================== ========================================================
Thibault Le Meur <Thibault.LeMeur@supelec.fr> Thibault Le Meur <Thibault.LeMeur@supelec.fr>
@ -6,8 +6,19 @@
This document is a guide to using the M-Audio Audiophile USB (tm) device with This document is a guide to using the M-Audio Audiophile USB (tm) device with
ALSA and JACK. ALSA and JACK.
History
=======
* v1.4 - Thibault Le Meur (2007-07-11)
- Added Low Endianness nature of 16bits-modes
found by Hakan Lennestal <Hakan.Lennestal@brfsodrahamn.se>
- Modifying document structure
* v1.5 - Thibault Le Meur (2007-07-12)
- Added AC3/DTS passthru info
1 - Audiophile USB Specs and correct usage 1 - Audiophile USB Specs and correct usage
========================================== ==========================================
This part is a reminder of important facts about the functions and limitations This part is a reminder of important facts about the functions and limitations
of the device. of the device.
@ -25,18 +36,18 @@ The device has 4 audio interfaces, and 2 MIDI ports:
The internal DAC/ADC has the following characteristics: The internal DAC/ADC has the following characteristics:
* sample depth of 16 or 24 bits * sample depth of 16 or 24 bits
* sample rate from 8kHz to 96kHz * sample rate from 8kHz to 96kHz
* Two ports can't use different sample depths at the same time. Moreover, the * Two interfaces can't use different sample depths at the same time.
Audiophile USB documentation gives the following Warning: "Please exit any Moreover, the Audiophile USB documentation gives the following Warning:
audio application running before switching between bit depths" "Please exit any audio application running before switching between bit depths"
Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be
activated at the same time depending on the audio mode selected: activated at the same time depending on the audio mode selected:
* 16-bit/48kHz ==> 4 channels in/4 channels out * 16-bit/48kHz ==> 4 channels in + 4 channels out
- Ai+Ao+Di+Do - Ai+Ao+Di+Do
* 24-bit/48kHz ==> 4 channels in/2 channels out, * 24-bit/48kHz ==> 4 channels in + 2 channels out,
or 2 channels in/4 channels out or 2 channels in + 4 channels out
- Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
* 24-bit/96kHz ==> 2 channels in, or 2 channels out (half duplex only) * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
- Ai or Ao or Di or Do - Ai or Ao or Di or Do
Important facts about the Digital interface: Important facts about the Digital interface:
@ -52,44 +63,56 @@ source is connected
synchronization error (for instance sound played at an odd sample rate) synchronization error (for instance sound played at an odd sample rate)
2 - Audiophile USB support in ALSA 2 - Audiophile USB MIDI support in ALSA
================================== =======================================
2.1 - MIDI ports The Audiophile USB MIDI ports will be automatically supported once the
----------------
The Audiophile USB MIDI ports will be automatically supported once the
following modules have been loaded: following modules have been loaded:
* snd-usb-audio * snd-usb-audio
* snd-seq-midi * snd-seq-midi
No additional setting is required. No additional setting is required.
2.2 - Audio ports
----------------- 3 - Audiophile USB Audio support in ALSA
========================================
Audio functions of the Audiophile USB device are handled by the snd-usb-audio Audio functions of the Audiophile USB device are handled by the snd-usb-audio
module. This module can work in a default mode (without any device-specific module. This module can work in a default mode (without any device-specific
parameter), or in an "advanced" mode with the device-specific parameter called parameter), or in an "advanced" mode with the device-specific parameter called
"device_setup". "device_setup".
2.2.1 - Default Alsa driver mode 3.1 - Default Alsa driver mode
------------------------------
The default behavior of the snd-usb-audio driver is to parse the device The default behavior of the snd-usb-audio driver is to list the device
capabilities at startup and enable all functions inside the device (including capabilities at startup and activate the required mode when required
all ports at any supported sample rates and sample depths). This approach by the applications: for instance if the user is recording in a
has the advantage to let the driver easily switch from sample rates/depths 24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
automatically according to the need of the application claiming the device. the snd-usb-audio module will reconfigure the device on the fly.
In this case the Audiophile ports are mapped to alsa pcm devices in the This approach has the advantage to let the driver automatically switch from sample
following way (I suppose the device's index is 1): rates/depths automatically according to the user's needs. However, those who
are using the device under windows know that this is not how the device is meant to
work: under windows applications must be closed before using the m-audio control
panel to switch the device working mode. Thus as we'll see in next section, this
Default Alsa driver mode can lead to device misconfigurations.
Let's get back to the Default Alsa driver mode for now. In this case the
Audiophile interfaces are mapped to alsa pcm devices in the following
way (I suppose the device's index is 1):
* hw:1,0 is Ao in playback and Di in capture * hw:1,0 is Ao in playback and Di in capture
* hw:1,1 is Do in playback and Ai in capture * hw:1,1 is Do in playback and Ai in capture
* hw:1,2 is Do in AC3/DTS passthrough mode * hw:1,2 is Do in AC3/DTS passthrough mode
You must note as well that the device uses Big Endian byte encoding so that In this mode, the device uses Big Endian byte-encoding so that
supported audio format are S16_BE for 16-bit depth modes and S24_3BE for supported audio format are S16_BE for 16-bit depth modes and S24_3BE for
24-bits depth mode. One exception is the hw:1,2 port which is Little Endian 24-bits depth mode.
compliant and thus uses S16_LE.
One exception is the hw:1,2 port which was reported to be Little Endian
compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface
is reported to be big endian in this default driver mode.
Examples: Examples:
* playing a S24_3BE encoded raw file to the Ao port * playing a S24_3BE encoded raw file to the Ao port
@ -98,22 +121,26 @@ Examples:
% arecord -D hw:1,1 -c2 -t raw -r48000 -fS24_3BE test.raw % arecord -D hw:1,1 -c2 -t raw -r48000 -fS24_3BE test.raw
* playing a S16_BE encoded raw file to the Do port * playing a S16_BE encoded raw file to the Do port
% aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
* playing an ac3 sample file to the Do port
% aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
If you're happy with the default Alsa driver setup and don't experience any If you're happy with the default Alsa driver mode and don't experience any
issue with this mode, then you can skip the following chapter. issue with this mode, then you can skip the following chapter.
2.2.2 - Advanced module setup 3.2 - Advanced module setup
---------------------------
Due to the hardware constraints described above, the device initialization made Due to the hardware constraints described above, the device initialization made
by the Alsa driver in default mode may result in a corrupted state of the by the Alsa driver in default mode may result in a corrupted state of the
device. For instance, a particularly annoying issue is that the sound captured device. For instance, a particularly annoying issue is that the sound captured
from the Ai port sounds distorted (as if boosted with an excessive high volume from the Ai interface sounds distorted (as if boosted with an excessive high
gain). volume gain).
For people having this problem, the snd-usb-audio module has a new module For people having this problem, the snd-usb-audio module has a new module
parameter called "device_setup". parameter called "device_setup" (this parameter was introduced in kernel
release 2.6.17)
2.2.2.1 - Initializing the working mode of the Audiophile USB 3.2.1 - Initializing the working mode of the Audiophile USB
As far as the Audiophile USB device is concerned, this value let the user As far as the Audiophile USB device is concerned, this value let the user
specify: specify:
@ -121,33 +148,57 @@ specify:
* the sample rate * the sample rate
* whether the Di port is used or not * whether the Di port is used or not
Here is a list of supported device_setup values for this device: When initialized with "device_setup=0x00", the snd-usb-audio module has
* device_setup=0x00 (or omitted) the same behaviour as when the parameter is omitted (see paragraph "Default
- Alsa driver default mode Alsa driver mode" above)
- maintains backward compatibility with setups that do not use this
parameter by not introducing any change Others modes are described in the following subsections.
- results sometimes in corrupted sound as described earlier
3.2.1.1 - 16-bit modes
The two supported modes are:
* device_setup=0x01 * device_setup=0x01
- 16bits 48kHz mode with Di disabled - 16bits 48kHz mode with Di disabled
- Ai,Ao,Do can be used at the same time - Ai,Ao,Do can be used at the same time
- hw:1,0 is not available in capture mode - hw:1,0 is not available in capture mode
- hw:1,2 is not available - hw:1,2 is not available
* device_setup=0x11 * device_setup=0x11
- 16bits 48kHz mode with Di enabled - 16bits 48kHz mode with Di enabled
- Ai,Ao,Di,Do can be used at the same time - Ai,Ao,Di,Do can be used at the same time
- hw:1,0 is available in capture mode - hw:1,0 is available in capture mode
- hw:1,2 is not available - hw:1,2 is not available
In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
the devices where reported to be Big-Endian when in fact they were Little-Endian
so that playing a file was a matter of using:
% aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
where "test_S16_LE.raw" was in fact a little-endian sample file.
Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
these modes) a fix has been committed (expected in kernel 2.6.23) and
Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
using:
% aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
3.2.1.2 - 24-bit modes
The three supported modes are:
* device_setup=0x09 * device_setup=0x09
- 24bits 48kHz mode with Di disabled - 24bits 48kHz mode with Di disabled
- Ai,Ao,Do can be used at the same time - Ai,Ao,Do can be used at the same time
- hw:1,0 is not available in capture mode - hw:1,0 is not available in capture mode
- hw:1,2 is not available - hw:1,2 is not available
* device_setup=0x19 * device_setup=0x19
- 24bits 48kHz mode with Di enabled - 24bits 48kHz mode with Di enabled
- 3 ports from {Ai,Ao,Di,Do} can be used at the same time - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
- hw:1,0 is available in capture mode and an active digital source must be - hw:1,0 is available in capture mode and an active digital source must be
connected to Di connected to Di
- hw:1,2 is not available - hw:1,2 is not available
* device_setup=0x0D or 0x10 * device_setup=0x0D or 0x10
- 24bits 96kHz mode - 24bits 96kHz mode
- Di is enabled by default for this mode but does not need to be connected - Di is enabled by default for this mode but does not need to be connected
@ -155,34 +206,64 @@ Here is a list of supported device_setup values for this device:
- Only 1 port from {Ai,Ao,Di,Do} can be used at the same time - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
- hw:1,0 is available in captured mode - hw:1,0 is available in captured mode
- hw:1,2 is not available - hw:1,2 is not available
In these modes the device is only Big-Endian compliant (see "Default Alsa driver
mode" above for an aplay command example)
3.2.1.3 - AC3 w/ DTS passthru mode
Thanks to Hakan Lennestal, I now have a report saying that this mode works.
* device_setup=0x03 * device_setup=0x03
- 16bits 48kHz mode with only the Do port enabled - 16bits 48kHz mode with only the Do port enabled
- AC3 with DTS passthru (not tested) - AC3 with DTS passthru
- Caution with this setup the Do port is mapped to the pcm device hw:1,0 - Caution with this setup the Do port is mapped to the pcm device hw:1,0
2.2.2.2 - Setting and switching configurations with the device_setup parameter The command line used to playback the AC3/DTS encoded .wav-files in this mode:
% aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
3.2.2 - How to use the device_setup parameter
----------------------------------------------
The parameter can be given: The parameter can be given:
* By manually probing the device (as root): * By manually probing the device (as root):
# modprobe -r snd-usb-audio # modprobe -r snd-usb-audio
# modprobe snd-usb-audio index=1 device_setup=0x09 # modprobe snd-usb-audio index=1 device_setup=0x09
* Or while configuring the modules options in your modules configuration file * Or while configuring the modules options in your modules configuration file
- For Fedora distributions, edit the /etc/modprobe.conf file: - For Fedora distributions, edit the /etc/modprobe.conf file:
alias snd-card-1 snd-usb-audio alias snd-card-1 snd-usb-audio
options snd-usb-audio index=1 device_setup=0x09 options snd-usb-audio index=1 device_setup=0x09
IMPORTANT NOTE WHEN SWITCHING CONFIGURATION: CAUTION when initializaing the device
------------------------------------------- -------------------------------------
* You may need to _first_ initialize the module with the correct device_setup
parameter and _only_after_ turn on the Audiophile USB device * Correct initialization on the device requires that device_setup is given to
* This is especially true when switching the sample depth: the module BEFORE the device is turned on. So, if you use the "manual probing"
method described above, take care to power-on the device AFTER this initialization.
* Failing to respect this will lead in a misconfiguration of the device. In this case
turn off the device, unproble the snd-usb-audio module, then probe it again with
correct device_setup parameter and then (and only then) turn on the device again.
* If you've correctly initialized the device in a valid mode and then want to switch
to another mode (possibly with another sample-depth), please use also the following
procedure:
- first turn off the device - first turn off the device
- de-register the snd-usb-audio module (modprobe -r) - de-register the snd-usb-audio module (modprobe -r)
- change the device_setup parameter by changing the device_setup - change the device_setup parameter by changing the device_setup
option in /etc/modprobe.conf option in /etc/modprobe.conf
- turn on the device - turn on the device
* A workaround for this last issue has been applied to kernel 2.6.23, but it may not
be enough to ensure the 'stability' of the device initialization.
2.2.2.3 - Audiophile USB's device_setup structure 3.2.3 - Technical details for hackers
-------------------------------------
This section is for hackers, wanting to understand details about the device
internals and how Alsa supports it.
3.2.3.1 - Audiophile USB's device_setup structure
If you want to understand the device_setup magic numbers for the Audiophile If you want to understand the device_setup magic numbers for the Audiophile
USB, you need some very basic understanding of binary computation. However, USB, you need some very basic understanding of binary computation. However,
@ -228,12 +309,12 @@ Caution:
- choosing b2 will prepare all interfaces for 24bits/96kHz but you'll - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
only be able to use one at the same time only be able to use one at the same time
2.2.3 - USB implementation details for this device 3.2.3.2 - USB implementation details for this device
You may safely skip this section if you're not interested in driver You may safely skip this section if you're not interested in driver
development. hacking.
This section describes some internal aspects of the device and summarize the This section describes some internal aspects of the device and summarizes the
data I got by usb-snooping the windows and Linux drivers. data I got by usb-snooping the windows and Linux drivers.
The M-Audio Audiophile USB has 7 USB Interfaces: The M-Audio Audiophile USB has 7 USB Interfaces:
@ -293,43 +374,45 @@ parse_audio_endpoints function uses a quirk called
"audiophile_skip_setting_quirk" in order to prevent AltSettings not "audiophile_skip_setting_quirk" in order to prevent AltSettings not
corresponding to device_setup from being registered in the driver. corresponding to device_setup from being registered in the driver.
3 - Audiophile USB and Jack support 4 - Audiophile USB and Jack support
=================================== ===================================
This section deals with support of the Audiophile USB device in Jack. This section deals with support of the Audiophile USB device in Jack.
The main issue regarding this support is that the device is Big Endian
compliant.
3.1 - Using the plug alsa plugin There are 2 main potential issues when using Jackd with the device:
-------------------------------- * support for Big-Endian devices in 24-bit modes
* support for 4-in / 4-out channels
Jack doesn't directly support big endian devices. Thus, one way to have support 4.1 - Direct support in Jackd
for this device with Alsa is to use the Alsa "plug" converter. -----------------------------
Jack supports big endian devices only in recent versions (thanks to
Andreas Steinmetz for his first big-endian patch). I can't remember
extacly when this support was released into jackd, let's just say that
with jackd version 0.103.0 it's almost ok (just a small bug is affecting
16bits Big-Endian devices, but since you've read carefully the above
paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices
are now Little Endians ;-) ).
You can run jackd with the following command for playback with Ao and
record with Ai:
% jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
4.2 - Using Alsa plughw
-----------------------
If you don't have a recent Jackd installed, you can downgrade to using
the Alsa "plug" converter.
For instance here is one way to run Jack with 2 playback channels on Ao and 2 For instance here is one way to run Jack with 2 playback channels on Ao and 2
capture channels from Ai: capture channels from Ai:
% jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1 % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
However you may see the following warning message: However you may see the following warning message:
"You appear to be using the ALSA software "plug" layer, probably a result of "You appear to be using the ALSA software "plug" layer, probably a result of
using the "default" ALSA device. This is less efficient than it could be. using the "default" ALSA device. This is less efficient than it could be.
Consider using a hardware device instead rather than using the plug layer." Consider using a hardware device instead rather than using the plug layer."
3.2 - Patching alsa to use direct pcm device 4.3 - Getting 2 input and/or output interfaces in Jack
--------------------------------------------
A patch for Jack by Andreas Steinmetz adds support for Big Endian devices.
However it has not been included in the CVS tree.
You can find it at the following URL:
http://sourceforge.net/tracker/index.php?func=detail&aid=1289682&group_id=39687&
atid=425939
After having applied the patch you can run jackd with the following command
line:
% jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
3.2 - Getting 2 input and/or output interfaces in Jack
------------------------------------------------------ ------------------------------------------------------
As you can see, starting the Jack server this way will only enable 1 stereo As you can see, starting the Jack server this way will only enable 1 stereo
@ -339,6 +422,7 @@ This is due to the following restrictions:
* Jack can only open one capture device and one playback device at a time * Jack can only open one capture device and one playback device at a time
* The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1 * The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
(and optionally hw:1,2) (and optionally hw:1,2)
If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
combine the Alsa devices into one logical "complex" device. combine the Alsa devices into one logical "complex" device.
@ -348,13 +432,11 @@ It is related to another device (ice1712) but can be adapted to suit
the Audiophile USB. the Audiophile USB.
Enabling multiple Audiophile USB interfaces for Jackd will certainly require: Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
* patching Jack with the previously mentioned "Big Endian" patch * Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
* patching Jackd with the MMAP_COMPLEX patch (see the ice1712 page) * (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
* patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
* define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc * define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
file file
* start jackd with this device * start jackd with this device
I had no success in testing this for now, but this may be due to my OS I had no success in testing this for now, if you have any success with this kind
configuration. If you have any success with this kind of setup, please of setup, please drop me an email.
drop me an email.

View File

@ -278,6 +278,21 @@ current mixer configuration by reading and writing the whole file
image. image.
Duplex Streams
==============
Note that when attempting to use a single device file for playback and
capture, the OSS API provides no way to set the format, sample rate or
number of channels different in each direction. Thus
io_handle = open("device", O_RDWR)
will only function correctly if the values are the same in each direction.
To use different values in the two directions, use both
input_handle = open("device", O_RDONLY)
output_handle = open("device", O_WRONLY)
and set the values for the corresponding handle.
Unsupported Features Unsupported Features
==================== ====================

View File

@ -115,9 +115,10 @@
#define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */ #define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */
#define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */
#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */ #define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */
#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */ #define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */ #define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
#define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */ #define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */
#define I2C_DRIVERID_LM4857 92 /* LM4857 Audio Amplifier */
#define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_I2CDEV 900
#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */

View File

@ -43,6 +43,7 @@ struct snd_ak4xxx_ops {
struct snd_akm4xxx_dac_channel { struct snd_akm4xxx_dac_channel {
char *name; /* mixer volume name */ char *name; /* mixer volume name */
unsigned int num_channels; unsigned int num_channels;
char *switch_name; /* mixer switch*/
}; };
/* ADC labels and channels */ /* ADC labels and channels */

View File

@ -1723,6 +1723,10 @@ struct snd_cs46xx {
struct snd_cs46xx_pcm *playback_pcm; struct snd_cs46xx_pcm *playback_pcm;
unsigned int play_ctl; unsigned int play_ctl;
#endif #endif
#ifdef CONFIG_PM
u32 *saved_regs;
#endif
}; };
int snd_cs46xx_create(struct snd_card *card, int snd_cs46xx_create(struct snd_card *card,

View File

@ -107,6 +107,7 @@ struct dsp_scb_descriptor {
char scb_name[DSP_MAX_SCB_NAME]; char scb_name[DSP_MAX_SCB_NAME];
u32 address; u32 address;
int index; int index;
u32 *data;
struct dsp_scb_descriptor * sub_list_ptr; struct dsp_scb_descriptor * sub_list_ptr;
struct dsp_scb_descriptor * next_scb_ptr; struct dsp_scb_descriptor * next_scb_ptr;
@ -127,6 +128,7 @@ struct dsp_task_descriptor {
int size; int size;
u32 address; u32 address;
int index; int index;
u32 *data;
}; };
struct dsp_pcm_channel_descriptor { struct dsp_pcm_channel_descriptor {

View File

@ -1120,6 +1120,16 @@
/************************************************************************************************/ /************************************************************************************************/
/* EMU1010m HANA Destinations */ /* EMU1010m HANA Destinations */
/************************************************************************************************/ /************************************************************************************************/
/* 32-bit destinations of signal in the Hana FPGA. Destinations are either
* physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
* - 16 x EMU_DST_ALICE2_EMU32_X.
*/
/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */
/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture.
* Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on
* setup of mixer control for each destination - see emumixer.c -
* snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
*/
#define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */ #define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */
#define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */ #define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */
#define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */ #define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */
@ -1199,6 +1209,12 @@
/************************************************************************************************/ /************************************************************************************************/
/* EMU1010m HANA Sources */ /* EMU1010m HANA Sources */
/************************************************************************************************/ /************************************************************************************************/
/* 32-bit sources of signal in the Hana FPGA. The sources are routed to
* destinations using mixer control for each destination - see emumixer.c
* Sources are either physical inputs of FPGA,
* or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A +
* 16 x EMU_SRC_ALICE_EMU32B
*/
#define EMU_SRC_SILENCE 0x0000 /* Silence */ #define EMU_SRC_SILENCE 0x0000 /* Silence */
#define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */ #define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */
#define EMU_SRC_DOCK_MIC_A2 0x0101 /* Audio Dock Mic A, 2nd or 96kHz */ #define EMU_SRC_DOCK_MIC_A2 0x0101 /* Audio Dock Mic A, 2nd or 96kHz */

View File

@ -38,6 +38,7 @@ enum sb_hw_type {
SB_HW_ALS100, /* Avance Logic ALS100 chip */ SB_HW_ALS100, /* Avance Logic ALS100 chip */
SB_HW_ALS4000, /* Avance Logic ALS4000 chip */ SB_HW_ALS4000, /* Avance Logic ALS4000 chip */
SB_HW_DT019X, /* Diamond Tech. DT-019X / Avance Logic ALS-007 */ SB_HW_DT019X, /* Diamond Tech. DT-019X / Avance Logic ALS-007 */
SB_HW_CS5530, /* Cyrix/NatSemi 5530 VSA1 */
}; };
#define SB_OPEN_PCM 0x01 #define SB_OPEN_PCM 0x01

View File

@ -1,3 +1,3 @@
/* include/version.h. Generated by alsa/ksync script. */ /* include/version.h. Generated by alsa/ksync script. */
#define CONFIG_SND_VERSION "1.0.14" #define CONFIG_SND_VERSION "1.0.14"
#define CONFIG_SND_DATE " (Thu May 31 09:03:25 2007 UTC)" #define CONFIG_SND_DATE " (Fri Jul 20 09:12:58 2007 UTC)"

View File

@ -1,9 +0,0 @@
#ifndef __SOUND_WAVEFRONT_FX_H
#define __SOUND_WAVEFRONT_FX_H
extern int snd_wavefront_fx_detect (snd_wavefront_t *);
extern void snd_wavefront_fx_ioctl (snd_synth_t *sdev,
unsigned int cmd,
unsigned long arg);
#endif __SOUND_WAVEFRONT_FX_H

View File

@ -65,6 +65,8 @@ source "sound/arm/Kconfig"
source "sound/mips/Kconfig" source "sound/mips/Kconfig"
source "sound/sh/Kconfig"
# the following will depend on the order of config. # the following will depend on the order of config.
# here assuming USB is defined before ALSA # here assuming USB is defined before ALSA
source "sound/usb/Kconfig" source "sound/usb/Kconfig"

View File

@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_DMASOUND) += oss/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
obj-$(CONFIG_SND_AOA) += aoa/ obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out # This one must be compilable even if sound is configured out

View File

@ -661,7 +661,7 @@ static struct transfer_info onyx_transfers[] = {
.tag = 2, .tag = 2,
}, },
#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
Once alsa gets supports for this kind of thing we can add it... /* Once alsa gets supports for this kind of thing we can add it... */
{ {
/* digital compressed output */ /* digital compressed output */
.formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
@ -713,7 +713,7 @@ static int onyx_prepare(struct codec_info_item *cii,
if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
/* mute and lock analog output */ /* mute and lock analog output */
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
if (onyx_write_register(onyx if (onyx_write_register(onyx,
ONYX_REG_DAC_CONTROL, ONYX_REG_DAC_CONTROL,
v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
goto out_unlock; goto out_unlock;

View File

@ -1487,7 +1487,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
/* resume pause */ /* resume pause */
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
snd_pcm_pause(substream, 0); snd_pcm_pause(substream, 0);
/* pre-start/stop - all running streams are changed to DRAINING state */ /* pre-start/stop - all running streams are changed to DRAINING state */

View File

@ -109,7 +109,7 @@ void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
spin_lock_irqsave(&list->lock, flags); spin_lock_irqsave(&list->lock, flags);
while (instr->use) { while (instr->use) {
spin_unlock_irqrestore(&list->lock, flags); spin_unlock_irqrestore(&list->lock, flags);
schedule_timeout_interruptible(1); schedule_timeout(1);
spin_lock_irqsave(&list->lock, flags); spin_lock_irqsave(&list->lock, flags);
} }
spin_unlock_irqrestore(&list->lock, flags); spin_unlock_irqrestore(&list->lock, flags);
@ -199,7 +199,7 @@ int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
instr = flist; instr = flist;
flist = instr->next; flist = instr->next;
while (instr->use) while (instr->use)
schedule_timeout_interruptible(1); schedule_timeout(1);
if (snd_seq_instr_free(instr, atomic)<0) if (snd_seq_instr_free(instr, atomic)<0)
snd_printk(KERN_WARNING "instrument free problem\n"); snd_printk(KERN_WARNING "instrument free problem\n");
instr = next; instr = next;
@ -555,7 +555,7 @@ static int instr_free(struct snd_seq_kinstr_ops *ops,
SNDRV_SEQ_INSTR_NOTIFY_REMOVE); SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
while (instr->use) { while (instr->use) {
spin_unlock_irqrestore(&list->lock, flags); spin_unlock_irqrestore(&list->lock, flags);
schedule_timeout_interruptible(1); schedule_timeout(1);
spin_lock_irqsave(&list->lock, flags); spin_lock_irqsave(&list->lock, flags);
} }
spin_unlock_irqrestore(&list->lock, flags); spin_unlock_irqrestore(&list->lock, flags);

View File

@ -1549,9 +1549,11 @@ static int snd_timer_user_info(struct file *file,
int err = 0; int err = 0;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
t = tu->timeri->timer; t = tu->timeri->timer;
snd_assert(t != NULL, return -ENXIO); if (!t)
return -EBADFD;
info = kzalloc(sizeof(*info), GFP_KERNEL); info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info) if (! info)
@ -1579,9 +1581,11 @@ static int snd_timer_user_params(struct file *file,
int err; int err;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
t = tu->timeri->timer; t = tu->timeri->timer;
snd_assert(t != NULL, return -ENXIO); if (!t)
return -EBADFD;
if (copy_from_user(&params, _params, sizeof(params))) if (copy_from_user(&params, _params, sizeof(params)))
return -EFAULT; return -EFAULT;
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
@ -1675,7 +1679,8 @@ static int snd_timer_user_status(struct file *file,
struct snd_timer_status status; struct snd_timer_status status;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
memset(&status, 0, sizeof(status)); memset(&status, 0, sizeof(status));
status.tstamp = tu->tstamp; status.tstamp = tu->tstamp;
status.resolution = snd_timer_resolution(tu->timeri); status.resolution = snd_timer_resolution(tu->timeri);
@ -1695,7 +1700,8 @@ static int snd_timer_user_start(struct file *file)
struct snd_timer_user *tu; struct snd_timer_user *tu;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
snd_timer_stop(tu->timeri); snd_timer_stop(tu->timeri);
tu->timeri->lost = 0; tu->timeri->lost = 0;
tu->last_resolution = 0; tu->last_resolution = 0;
@ -1708,7 +1714,8 @@ static int snd_timer_user_stop(struct file *file)
struct snd_timer_user *tu; struct snd_timer_user *tu;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
} }
@ -1718,7 +1725,8 @@ static int snd_timer_user_continue(struct file *file)
struct snd_timer_user *tu; struct snd_timer_user *tu;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
tu->timeri->lost = 0; tu->timeri->lost = 0;
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
} }
@ -1729,7 +1737,8 @@ static int snd_timer_user_pause(struct file *file)
struct snd_timer_user *tu; struct snd_timer_user *tu;
tu = file->private_data; tu = file->private_data;
snd_assert(tu->timeri != NULL, return -ENXIO); if (!tu->timeri)
return -EBADFD;
return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0; return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
} }

View File

@ -659,7 +659,7 @@ static struct platform_driver snd_dummy_driver = {
}, },
}; };
static void __init_or_module snd_dummy_unregister_all(void) static void snd_dummy_unregister_all(void)
{ {
int i; int i;

View File

@ -228,7 +228,7 @@ static struct pnp_driver snd_mpu401_pnp_driver = {
static struct pnp_driver snd_mpu401_pnp_driver; static struct pnp_driver snd_mpu401_pnp_driver;
#endif #endif
static void __init_or_module snd_mpu401_unregister_all(void) static void snd_mpu401_unregister_all(void)
{ {
int i; int i;

View File

@ -833,7 +833,7 @@ static struct platform_driver snd_portman_driver = {
/********************************************************************* /*********************************************************************
* module init stuff * module init stuff
*********************************************************************/ *********************************************************************/
static void __init_or_module snd_portman_unregister_all(void) static void snd_portman_unregister_all(void)
{ {
int i; int i;

View File

@ -998,7 +998,7 @@ static struct platform_driver snd_serial_driver = {
}, },
}; };
static void __init_or_module snd_serial_unregister_all(void) static void snd_serial_unregister_all(void)
{ {
int i; int i;

View File

@ -145,7 +145,7 @@ static struct platform_driver snd_virmidi_driver = {
}, },
}; };
static void __init_or_module snd_virmidi_unregister_all(void) static void snd_virmidi_unregister_all(void)
{ {
int i; int i;

View File

@ -481,8 +481,8 @@ static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
int addr = AK_GET_ADDR(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value); int shift = AK_GET_SHIFT(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value); int invert = AK_GET_INVERT(kcontrol->private_value);
unsigned char val = snd_akm4xxx_get(ak, chip, addr); /* we observe the (1<<shift) bit only */
unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
if (invert) if (invert)
val = ! val; val = ! val;
ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0; ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
@ -585,6 +585,26 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
mixer_ch = 0; mixer_ch = 0;
for (idx = 0; idx < ak->num_dacs; ) { for (idx = 0; idx < ak->num_dacs; ) {
/* mute control for Revolution 7.1 - AK4381 */
if (ak->type == SND_AK4381
&& ak->dac_info[mixer_ch].switch_name) {
memset(&knew, 0, sizeof(knew));
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.count = 1;
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
knew.name = ak->dac_info[mixer_ch].switch_name;
knew.info = ak4xxx_switch_info;
knew.get = ak4xxx_switch_get;
knew.put = ak4xxx_switch_put;
knew.access = 0;
/* register 1, bit 0 (SMUTE): 0 = normal operation,
1 = mute */
knew.private_value =
AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
}
memset(&knew, 0, sizeof(knew)); memset(&knew, 0, sizeof(knew));
if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) { if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
knew.name = "DAC Volume"; knew.name = "DAC Volume";

View File

@ -1,8 +1,5 @@
# ALSA ISA drivers # ALSA ISA drivers
menu "ISA devices"
depends on SND!=n && ISA && ISA_DMA_API
config SND_AD1848_LIB config SND_AD1848_LIB
tristate tristate
select SND_PCM select SND_PCM
@ -11,6 +8,22 @@ config SND_CS4231_LIB
tristate tristate
select SND_PCM select SND_PCM
config SND_SB_COMMON
tristate
config SND_SB8_DSP
tristate
select SND_PCM
select SND_SB_COMMON
config SND_SB16_DSP
tristate
select SND_PCM
select SND_SB_COMMON
menu "ISA devices"
depends on SND!=n && ISA && ISA_DMA_API
config SND_ADLIB config SND_ADLIB
tristate "AdLib FM card" tristate "AdLib FM card"
depends on SND depends on SND
@ -55,7 +68,7 @@ config SND_ALS100
select ISAPNP select ISAPNP
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_SB16_DSP
help help
Say Y here to include support for soundcards based on Avance Say Y here to include support for soundcards based on Avance
Logic ALS100, ALS110, ALS120 and ALS200 chips. Logic ALS100, ALS110, ALS120 and ALS200 chips.
@ -81,6 +94,7 @@ config SND_CMI8330
tristate "C-Media CMI8330" tristate "C-Media CMI8330"
depends on SND depends on SND
select SND_AD1848_LIB select SND_AD1848_LIB
select SND_SB16_DSP
help help
Say Y here to include support for soundcards based on the Say Y here to include support for soundcards based on the
C-Media CMI8330 chip. C-Media CMI8330 chip.
@ -132,7 +146,7 @@ config SND_DT019X
select ISAPNP select ISAPNP
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_SB16_DSP
help help
Say Y here to include support for soundcards based on the Say Y here to include support for soundcards based on the
Diamond Technologies DT-019X or Avance Logic ALS-007 chips. Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
@ -145,7 +159,7 @@ config SND_ES968
depends on SND && PNP && ISA depends on SND && PNP && ISA
select ISAPNP select ISAPNP
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_SB8_DSP
help help
Say Y here to include support for ESS AudioDrive ES968 chips. Say Y here to include support for ESS AudioDrive ES968 chips.
@ -321,7 +335,7 @@ config SND_SB8
depends on SND depends on SND
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_RAWMIDI select SND_RAWMIDI
select SND_PCM select SND_SB8_DSP
help help
Say Y here to include support for Creative Sound Blaster 1.0/ Say Y here to include support for Creative Sound Blaster 1.0/
2.0/Pro (8-bit) or 100% compatible soundcards. 2.0/Pro (8-bit) or 100% compatible soundcards.
@ -334,7 +348,7 @@ config SND_SB16
depends on SND depends on SND
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_SB16_DSP
help help
Say Y here to include support for Sound Blaster 16 soundcards Say Y here to include support for Sound Blaster 16 soundcards
(including the Plug and Play version). (including the Plug and Play version).
@ -347,7 +361,7 @@ config SND_SBAWE
depends on SND depends on SND
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_SB16_DSP
help help
Say Y here to include support for Sound Blaster AWE soundcards Say Y here to include support for Sound Blaster AWE soundcards
(including the Plug and Play version). (including the Plug and Play version).

View File

@ -245,7 +245,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n"); snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
return; return;
} }
time = schedule_timeout_interruptible(time); time = schedule_timeout(time);
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
} }
#if 0 #if 0
@ -258,7 +258,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
return; return;
} }
time = schedule_timeout_interruptible(time); time = schedule_timeout(time);
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
} }
spin_unlock_irqrestore(&chip->reg_lock, flags); spin_unlock_irqrestore(&chip->reg_lock, flags);

View File

@ -164,6 +164,8 @@ static struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
{ .id = "YMH0801", .devs = { { "YMH0021" } } }, { .id = "YMH0801", .devs = { { "YMH0021" } } },
/* NeoMagic MagicWave 3DX */ /* NeoMagic MagicWave 3DX */
{ .id = "NMX2200", .devs = { { "YMH2210" } } }, { .id = "NMX2200", .devs = { { "YMH2210" } } },
/* NeoMagic MagicWave 3D */
{ .id = "NMX2200", .devs = { { "NMX2210" } } },
/* --- */ /* --- */
{ .id = "" } /* end */ { .id = "" } /* end */
}; };

View File

@ -1927,10 +1927,12 @@ static struct snd_card *snd_opti9xx_card_new(void)
static int __devinit snd_opti9xx_isa_match(struct device *devptr, static int __devinit snd_opti9xx_isa_match(struct device *devptr,
unsigned int dev) unsigned int dev)
{ {
#ifdef CONFIG_PNP
if (snd_opti9xx_pnp_is_probed) if (snd_opti9xx_pnp_is_probed)
return 0; return 0;
if (isapnp) if (isapnp)
return 0; return 0;
#endif
return 1; return 1;
} }
@ -2096,6 +2098,7 @@ static int __init alsa_card_opti9xx_init(void)
pnp_register_card_driver(&opti9xx_pnpc_driver); pnp_register_card_driver(&opti9xx_pnpc_driver);
if (snd_opti9xx_pnp_is_probed) if (snd_opti9xx_pnp_is_probed)
return 0; return 0;
pnp_unregister_card_driver(&opti9xx_pnpc_driver);
#endif #endif
return isa_register_driver(&snd_opti9xx_driver, 1); return isa_register_driver(&snd_opti9xx_driver, 1);
} }

View File

@ -22,14 +22,13 @@ snd-es968-objs := es968.o
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB8) += snd-sb8.o
obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SB16) += snd-sb16.o
obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o obj-$(CONFIG_SND_ES968) += snd-es968.o
obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
ifeq ($(CONFIG_SND_SB16_CSP),y) ifeq ($(CONFIG_SND_SB16_CSP),y)
obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o

View File

@ -563,6 +563,11 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
__open_ok: __open_ok:
if (chip->hardware == SB_HW_ALS100) if (chip->hardware == SB_HW_ALS100)
runtime->hw.rate_max = 48000; runtime->hw.rate_max = 48000;
if (chip->hardware == SB_HW_CS5530) {
runtime->hw.buffer_bytes_max = 32 * 1024;
runtime->hw.periods_min = 2;
runtime->hw.rate_min = 44100;
}
if (chip->mode & SB_RATE_LOCK) if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->playback_substream = substream; chip->playback_substream = substream;
@ -633,6 +638,11 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
__open_ok: __open_ok:
if (chip->hardware == SB_HW_ALS100) if (chip->hardware == SB_HW_ALS100)
runtime->hw.rate_max = 48000; runtime->hw.rate_max = 48000;
if (chip->hardware == SB_HW_CS5530) {
runtime->hw.buffer_bytes_max = 32 * 1024;
runtime->hw.periods_min = 2;
runtime->hw.rate_min = 44100;
}
if (chip->mode & SB_RATE_LOCK) if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->capture_substream = substream; chip->capture_substream = substream;

View File

@ -128,7 +128,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
minor = version & 0xff; minor = version & 0xff;
snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
chip->port, major, minor); chip->port, major, minor);
switch (chip->hardware) { switch (chip->hardware) {
case SB_HW_AUTO: case SB_HW_AUTO:
switch (major) { switch (major) {
@ -168,6 +168,9 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
case SB_HW_DT019X: case SB_HW_DT019X:
str = "(DT019X/ALS007)"; str = "(DT019X/ALS007)";
break; break;
case SB_HW_CS5530:
str = "16 (CS5530)";
break;
default: default:
return -ENODEV; return -ENODEV;
} }

View File

@ -821,6 +821,7 @@ int snd_sbmixer_new(struct snd_sb *chip)
break; break;
case SB_HW_16: case SB_HW_16:
case SB_HW_ALS100: case SB_HW_ALS100:
case SB_HW_CS5530:
if ((err = snd_sbmixer_init(chip, if ((err = snd_sbmixer_init(chip,
snd_sb16_controls, snd_sb16_controls,
ARRAY_SIZE(snd_sb16_controls), ARRAY_SIZE(snd_sb16_controls),
@ -950,6 +951,7 @@ void snd_sbmixer_suspend(struct snd_sb *chip)
break; break;
case SB_HW_16: case SB_HW_16:
case SB_HW_ALS100: case SB_HW_ALS100:
case SB_HW_CS5530:
save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
break; break;
case SB_HW_ALS4000: case SB_HW_ALS4000:
@ -975,6 +977,7 @@ void snd_sbmixer_resume(struct snd_sb *chip)
break; break;
case SB_HW_16: case SB_HW_16:
case SB_HW_ALS100: case SB_HW_ALS100:
case SB_HW_CS5530:
restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
break; break;
case SB_HW_ALS4000: case SB_HW_ALS4000:

View File

@ -382,7 +382,7 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long flags; unsigned long flags;
unsigned char x; unsigned char x;
schedule_timeout_interruptible(1); schedule_timeout(1);
spin_lock_irqsave(&s->lock, flags); spin_lock_irqsave(&s->lock, flags);
x = inb(HOST_DATA_IO(s->io_base)); x = inb(HOST_DATA_IO(s->io_base));
@ -409,7 +409,7 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long flags; unsigned long flags;
unsigned char x; unsigned char x;
schedule_timeout_interruptible(1); schedule_timeout(1);
spin_lock_irqsave(&s->lock, flags); spin_lock_irqsave(&s->lock, flags);
x = inb(HOST_DATA_IO(s->io_base)); x = inb(HOST_DATA_IO(s->io_base));

View File

@ -1780,7 +1780,7 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
outb (val,port); outb (val,port);
spin_unlock_irq(&dev->irq_lock); spin_unlock_irq(&dev->irq_lock);
while (1) { while (1) {
if ((timeout = schedule_timeout_interruptible(timeout)) == 0) if ((timeout = schedule_timeout(timeout)) == 0)
return; return;
if (dev->irq_ok) if (dev->irq_ok)
return; return;

View File

@ -33,6 +33,7 @@ config SND_ALS4000
select SND_OPL3_LIB select SND_OPL3_LIB
select SND_MPU401_UART select SND_MPU401_UART
select SND_PCM select SND_PCM
select SND_SB_COMMON
help help
Say Y here to include support for soundcards based on Avance Logic Say Y here to include support for soundcards based on Avance Logic
ALS4000 chips. ALS4000 chips.
@ -215,6 +216,16 @@ config SND_CS46XX_NEW_DSP
This works better than the old code, so say Y. This works better than the old code, so say Y.
config SND_CS5530
tristate "CS5530 Audio"
depends on SND && ISA_DMA_API
select SND_SB16_DSP
help
Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
To compile this driver as a module, choose M here: the module
will be called snd-cs5530.
config SND_CS5535AUDIO config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio" tristate "CS5535/CS5536 Audio"
depends on SND && X86 && !X86_64 depends on SND && X86 && !X86_64

View File

@ -12,6 +12,7 @@ snd-azt3328-objs := azt3328.o
snd-bt87x-objs := bt87x.o snd-bt87x-objs := bt87x.o
snd-cmipci-objs := cmipci.o snd-cmipci-objs := cmipci.o
snd-cs4281-objs := cs4281.o snd-cs4281-objs := cs4281.o
snd-cs5530-objs := cs5530.o
snd-ens1370-objs := ens1370.o snd-ens1370-objs := ens1370.o
snd-ens1371-objs := ens1371.o snd-ens1371-objs := ens1371.o
snd-es1938-objs := es1938.o snd-es1938-objs := es1938.o
@ -36,6 +37,7 @@ obj-$(CONFIG_SND_AZT3328) += snd-azt3328.o
obj-$(CONFIG_SND_BT87X) += snd-bt87x.o obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
obj-$(CONFIG_SND_CS4281) += snd-cs4281.o obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
obj-$(CONFIG_SND_CS5530) += snd-cs5530.o
obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
obj-$(CONFIG_SND_ES1938) += snd-es1938.o obj-$(CONFIG_SND_ES1938) += snd-es1938.o

View File

@ -239,7 +239,7 @@ struct snd_ali_image {
struct snd_ali { struct snd_ali {
unsigned long irq; int irq;
unsigned long port; unsigned long port;
unsigned char revision; unsigned char revision;
@ -731,8 +731,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
return; return;
} }
count = 0; for (count = 0; count <= 50000; count++) {
while (count++ <= 50000) {
snd_ali_delay(codec, 6); snd_ali_delay(codec, 6);
bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
R2 = bval & 0x1F; R2 = bval & 0x1F;
@ -2343,7 +2342,7 @@ static int __devinit snd_ali_probe(struct pci_dev *pci,
strcpy(card->driver, "ALI5451"); strcpy(card->driver, "ALI5451");
strcpy(card->shortname, "ALI 5451"); strcpy(card->shortname, "ALI 5451");
sprintf(card->longname, "%s at 0x%lx, irq %li", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, codec->port, codec->irq); card->shortname, codec->port, codec->irq);
snd_ali_printk("register card.\n"); snd_ali_printk("register card.\n");

View File

@ -88,8 +88,8 @@
#define PLAYBACK_BLOCK_COUNTER 0x9A #define PLAYBACK_BLOCK_COUNTER 0x9A
#define RECORD_BLOCK_COUNTER 0x9B #define RECORD_BLOCK_COUNTER 0x9B
#define DEBUG_CALLS 1 #define DEBUG_CALLS 0
#define DEBUG_PLAY_REC 1 #define DEBUG_PLAY_REC 0
#if DEBUG_CALLS #if DEBUG_CALLS
#define snd_als300_dbgcalls(format, args...) printk(format, ##args) #define snd_als300_dbgcalls(format, args...) printk(format, ##args)
@ -733,7 +733,8 @@ static int __devinit snd_als300_create(struct snd_card *card,
snd_als300_init(chip); snd_als300_init(chip);
if (snd_als300_ac97(chip) < 0) { err = snd_als300_ac97(chip);
if (err < 0) {
snd_printk(KERN_WARNING "Could not create ac97\n"); snd_printk(KERN_WARNING "Could not create ac97\n");
snd_als300_free(chip); snd_als300_free(chip);
return err; return err;

View File

@ -168,6 +168,25 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
#include "ca0106.h" #include "ca0106.h"
static struct snd_ca0106_details ca0106_chip_details[] = { static struct snd_ca0106_details ca0106_chip_details[] = {
/* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
/* It is really just a normal SB Live 24bit. */
/*
* CTRL:CA0111-WTLF
* ADC: WM8775SEDS
* DAC: CS4382-KQZ
*/
/* Tested:
* Playback on front, rear, center/lfe speakers
* Capture from Mic in.
* Not-Tested:
* Capture from Line in.
* Playback to digital out.
*/
{ .serial = 0x10121102,
.name = "X-Fi Extreme Audio [SB0790]",
.gpio_type = 1,
.i2c_adc = 1 } ,
/* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */
/* AudigyLS[SB0310] */ /* AudigyLS[SB0310] */
{ .serial = 0x10021102, { .serial = 0x10021102,
.name = "AudigyLS [SB0310]", .name = "AudigyLS [SB0310]",

View File

@ -2897,6 +2897,10 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
} }
#endif #endif
#ifdef CONFIG_PM
kfree(chip->saved_regs);
#endif
pci_disable_device(chip->pci); pci_disable_device(chip->pci);
kfree(chip); kfree(chip);
return 0; return 0;
@ -3140,6 +3144,23 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
/* /*
* start and load DSP * start and load DSP
*/ */
static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
{
unsigned int tmp;
snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
tmp = snd_cs46xx_peek(chip, BA1_PFIE);
tmp &= ~0x0000f03f;
snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
tmp = snd_cs46xx_peek(chip, BA1_CIE);
tmp &= ~0x0000003f;
tmp |= 0x00000001;
snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
}
int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip) int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
{ {
unsigned int tmp; unsigned int tmp;
@ -3214,19 +3235,7 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
snd_cs46xx_proc_start(chip); snd_cs46xx_proc_start(chip);
/* cs46xx_enable_stream_irqs(chip);
* Enable interrupts on the part.
*/
snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
tmp = snd_cs46xx_peek(chip, BA1_PFIE);
tmp &= ~0x0000f03f;
snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
tmp = snd_cs46xx_peek(chip, BA1_CIE);
tmp &= ~0x0000003f;
tmp |= 0x00000001;
snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
#ifndef CONFIG_SND_CS46XX_NEW_DSP #ifndef CONFIG_SND_CS46XX_NEW_DSP
/* set the attenuation to 0dB */ /* set the attenuation to 0dB */
@ -3665,11 +3674,19 @@ static struct cs_card_type __devinitdata cards[] = {
* APM support * APM support
*/ */
#ifdef CONFIG_PM #ifdef CONFIG_PM
static unsigned int saved_regs[] = {
BA0_ACOSV,
BA0_ASER_FADDR,
BA0_ASER_MASTER,
BA1_PVOL,
BA1_CVOL,
};
int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct snd_cs46xx *chip = card->private_data; struct snd_cs46xx *chip = card->private_data;
int amp_saved; int i, amp_saved;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1; chip->in_suspend = 1;
@ -3680,6 +3697,10 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
/* save some registers */
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
amp_saved = chip->amplifier; amp_saved = chip->amplifier;
/* turn off amp */ /* turn off amp */
chip->amplifier_ctrl(chip, -chip->amplifier); chip->amplifier_ctrl(chip, -chip->amplifier);
@ -3698,7 +3719,7 @@ int snd_cs46xx_resume(struct pci_dev *pci)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct snd_cs46xx *chip = card->private_data; struct snd_cs46xx *chip = card->private_data;
int amp_saved; int i, amp_saved;
pci_set_power_state(pci, PCI_D0); pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci); pci_restore_state(pci);
@ -3716,6 +3737,16 @@ int snd_cs46xx_resume(struct pci_dev *pci)
snd_cs46xx_chip_init(chip); snd_cs46xx_chip_init(chip);
snd_cs46xx_reset(chip);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
cs46xx_dsp_resume(chip);
/* restore some registers */
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
#else
snd_cs46xx_download_image(chip);
#endif
#if 0 #if 0
snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE,
chip->ac97_general_purpose); chip->ac97_general_purpose);
@ -3730,6 +3761,13 @@ int snd_cs46xx_resume(struct pci_dev *pci)
snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
/* reset playback/capture */
snd_cs46xx_set_play_sample_rate(chip, 8000);
snd_cs46xx_set_capture_sample_rate(chip, 8000);
snd_cs46xx_proc_start(chip);
cs46xx_enable_stream_irqs(chip);
if (amp_saved) if (amp_saved)
chip->amplifier_ctrl(chip, 1); /* turn amp on */ chip->amplifier_ctrl(chip, 1); /* turn amp on */
else else
@ -3896,6 +3934,15 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
snd_cs46xx_proc_init(card, chip); snd_cs46xx_proc_init(card, chip);
#ifdef CONFIG_PM
chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
ARRAY_SIZE(saved_regs), GFP_KERNEL);
if (!chip->saved_regs) {
snd_cs46xx_free(chip);
return -ENOMEM;
}
#endif
chip->active_ctrl(chip, -1); /* disable CLKRUN */ chip->active_ctrl(chip, -1); /* disable CLKRUN */
snd_card_set_dev(card, &pci->dev); snd_card_set_dev(card, &pci->dev);

View File

@ -86,6 +86,9 @@ static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned
struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip); struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip); void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module); int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
#ifdef CONFIG_PM
int cs46xx_dsp_resume(struct snd_cs46xx * chip);
#endif
struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
int symbol_type); int symbol_type);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS

View File

@ -306,13 +306,59 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
mutex_unlock(&chip->spos_mutex); mutex_unlock(&chip->spos_mutex);
} }
static int dsp_load_parameter(struct snd_cs46xx *chip,
struct dsp_segment_desc *parameter)
{
u32 doffset, dsize;
if (!parameter) {
snd_printdd("dsp_spos: module got no parameter segment\n");
return 0;
}
doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
dsize = parameter->size * 4;
snd_printdd("dsp_spos: "
"downloading parameter data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
snd_printk(KERN_ERR "dsp_spos: "
"failed to download parameter data to DSP\n");
return -EINVAL;
}
return 0;
}
static int dsp_load_sample(struct snd_cs46xx *chip,
struct dsp_segment_desc *sample)
{
u32 doffset, dsize;
if (!sample) {
snd_printdd("dsp_spos: module got no sample segment\n");
return 0;
}
doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
dsize = sample->size * 4;
snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
return -EINVAL;
}
return 0;
}
int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module) int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
{ {
struct dsp_spos_instance * ins = chip->dsp_spos_instance; struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM); struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
u32 doffset, dsize; u32 doffset, dsize;
int err;
if (ins->nmodules == DSP_MAX_MODULES - 1) { if (ins->nmodules == DSP_MAX_MODULES - 1) {
snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n"); snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
@ -326,49 +372,20 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE); snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
} }
if (parameter == NULL) { err = dsp_load_parameter(chip, get_segment_desc(module,
snd_printdd("dsp_spos: module got no parameter segment\n"); SEGTYPE_SP_PARAMETER));
} else { if (err < 0)
if (ins->nmodules > 0) { return err;
snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
}
doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
dsize = parameter->size * 4;
snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
return -EINVAL;
}
}
if (ins->nmodules == 0) { if (ins->nmodules == 0) {
snd_printdd("dsp_spos: clearing sample area\n"); snd_printdd("dsp_spos: clearing sample area\n");
snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE); snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
} }
if (sample == NULL) { err = dsp_load_sample(chip, get_segment_desc(module,
snd_printdd("dsp_spos: module got no sample segment\n"); SEGTYPE_SP_SAMPLE));
} else { if (err < 0)
if (ins->nmodules > 0) { return err;
snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
}
doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
dsize = sample->size * 4;
snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
return -EINVAL;
}
}
if (ins->nmodules == 0) { if (ins->nmodules == 0) {
snd_printdd("dsp_spos: clearing code area\n"); snd_printdd("dsp_spos: clearing code area\n");
@ -986,7 +1003,10 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
return NULL; return NULL;
} }
strcpy(ins->tasks[ins->ntask].task_name,name); if (name)
strcpy(ins->tasks[ins->ntask].task_name, name);
else
strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].address = dest;
ins->tasks[ins->ntask].size = size; ins->tasks[ins->ntask].size = size;
@ -995,7 +1015,8 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
desc = (ins->tasks + ins->ntask); desc = (ins->tasks + ins->ntask);
ins->ntask++; ins->ntask++;
add_symbol (chip,name,dest,SYMBOL_PARAMETER); if (name)
add_symbol (chip,name,dest,SYMBOL_PARAMETER);
return desc; return desc;
} }
@ -1006,6 +1027,7 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
desc = _map_scb (chip,name,dest); desc = _map_scb (chip,name,dest);
if (desc) { if (desc) {
desc->data = scb_data;
_dsp_create_scb(chip,scb_data,dest); _dsp_create_scb(chip,scb_data,dest);
} else { } else {
snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n"); snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
@ -1023,6 +1045,7 @@ cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_da
desc = _map_task_tree (chip,name,dest,size); desc = _map_task_tree (chip,name,dest,size);
if (desc) { if (desc) {
desc->data = task_data;
_dsp_create_task_tree(chip,task_data,dest,size); _dsp_create_task_tree(chip,task_data,dest,size);
} else { } else {
snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n"); snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
@ -1320,8 +1343,10 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
0x0000ffff 0x0000ffff
}; };
/* dirty hack ... */ if (!cs46xx_dsp_create_task_tree(chip, NULL,
_dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2); (u32 *)&mix2_ostream_spb,
WRITE_BACK_SPB, 2))
goto _fail_end;
} }
/* input sample converter */ /* input sample converter */
@ -1622,7 +1647,6 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
return 0; return 0;
} }
static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip) static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
{ {
struct dsp_spos_instance * ins = chip->dsp_spos_instance; struct dsp_spos_instance * ins = chip->dsp_spos_instance;
@ -1894,3 +1918,61 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
return 0; return 0;
} }
#ifdef CONFIG_PM
int cs46xx_dsp_resume(struct snd_cs46xx * chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i, err;
/* clear parameter, sample and code areas */
snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET,
DSP_PARAMETER_BYTE_SIZE);
snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET,
DSP_SAMPLE_BYTE_SIZE);
snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
for (i = 0; i < ins->nmodules; i++) {
struct dsp_module_desc *module = &ins->modules[i];
struct dsp_segment_desc *seg;
u32 doffset, dsize;
seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER);
err = dsp_load_parameter(chip, seg);
if (err < 0)
return err;
seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE);
err = dsp_load_sample(chip, seg);
if (err < 0)
return err;
seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM);
if (!seg)
continue;
doffset = seg->offset * 4 + module->load_address * 4
+ DSP_CODE_BYTE_OFFSET;
dsize = seg->size * 4;
err = snd_cs46xx_download(chip,
ins->code.data + module->load_address,
doffset, dsize);
if (err < 0)
return err;
}
for (i = 0; i < ins->ntask; i++) {
struct dsp_task_descriptor *t = &ins->tasks[i];
_dsp_create_task_tree(chip, t->data, t->address, t->size);
}
for (i = 0; i < ins->nscb; i++) {
struct dsp_scb_descriptor *s = &ins->scbs[i];
if (s->deleted)
continue;
_dsp_create_scb(chip, s->data, s->address);
}
return 0;
}
#endif

306
sound/pci/cs5530.c Normal file
View File

@ -0,0 +1,306 @@
/*
* cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
*
* (C) Copyright 2007 Ash Willis <ashwillis@programmer.net>
* (C) Copyright 2003 Red Hat Inc <alan@redhat.com>
*
* This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did
* mess with it a bit. The chip seems to have to have trouble with full duplex
* mode. If we're recording in 8bit 8000kHz, say, and we then attempt to
* simultaneously play back audio at 16bit 44100kHz, the device actually plays
* back in the same format in which it is capturing. By forcing the chip to
* always play/capture in 16/44100, we can let alsa-lib convert the samples and
* that way we can hack up some full duplex audio.
*
* XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
* The older version (VSA1) provides fairly good soundblaster emulation
* although there are a couple of bugs: large DMA buffers break record,
* and the MPU event handling seems suspect. VSA2 allows the native driver
* to control the AC97 audio engine directly and requires a different driver.
*
* Thanks to National Semiconductor for providing the needed information
* on the XpressAudio(tm) internals.
*
* 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, 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.
*
* TO DO:
* Investigate whether we can portably support Cognac (5520) in the
* same manner.
*/
#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/initval.h>
MODULE_AUTHOR("Ash Willis");
MODULE_DESCRIPTION("CS5530 Audio");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
struct snd_cs5530 {
struct snd_card *card;
struct pci_dev *pci;
struct snd_sb *sb;
unsigned long pci_base;
};
static struct pci_device_id snd_cs5530_ids[] = {
{PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
PCI_ANY_ID, 0, 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
static int snd_cs5530_free(struct snd_cs5530 *chip)
{
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
return 0;
}
static int snd_cs5530_dev_free(struct snd_device *device)
{
struct snd_cs5530 *chip = device->device_data;
return snd_cs5530_free(chip);
}
static void __devexit snd_cs5530_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
{
outb(reg, io + 4);
udelay(20);
reg = inb(io + 5);
udelay(20);
return reg;
}
static int __devinit snd_cs5530_create(struct snd_card *card,
struct pci_dev *pci,
struct snd_cs5530 **rchip)
{
struct snd_cs5530 *chip;
unsigned long sb_base;
u8 irq, dma8, dma16 = 0;
u16 map;
void __iomem *mem;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_cs5530_dev_free,
};
*rchip = NULL;
err = pci_enable_device(pci);
if (err < 0)
return err;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL) {
pci_disable_device(pci);
return -ENOMEM;
}
chip->card = card;
chip->pci = pci;
err = pci_request_regions(pci, "CS5530");
if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->pci_base = pci_resource_start(pci, 0);
mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0));
if (mem == NULL) {
kfree(chip);
pci_disable_device(pci);
return -EBUSY;
}
map = readw(mem + 0x18);
iounmap(mem);
/* Map bits
0:1 * 0x20 + 0x200 = sb base
2 sb enable
3 adlib enable
5 MPU enable 0x330
6 MPU enable 0x300
The other bits may be used internally so must be masked */
sb_base = 0x220 + 0x20 * (map & 3);
if (map & (1<<2))
printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
else {
printk(KERN_ERR "Could not find XpressAudio!\n");
snd_cs5530_free(chip);
return -ENODEV;
}
if (map & (1<<5))
printk(KERN_INFO "CS5530: MPU at 0x300\n");
else if (map & (1<<6))
printk(KERN_INFO "CS5530: MPU at 0x330\n");
irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
if (dma8 & 0x20)
dma16 = 5;
else if (dma8 & 0x40)
dma16 = 6;
else if (dma8 & 0x80)
dma16 = 7;
else {
printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
snd_cs5530_free(chip);
return -ENODEV;
}
if (dma8 & 0x01)
dma8 = 0;
else if (dma8 & 02)
dma8 = 1;
else if (dma8 & 0x08)
dma8 = 3;
else {
printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
snd_cs5530_free(chip);
return -ENODEV;
}
if (irq & 1)
irq = 9;
else if (irq & 2)
irq = 5;
else if (irq & 4)
irq = 7;
else if (irq & 8)
irq = 10;
else {
printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
snd_cs5530_free(chip);
return -ENODEV;
}
printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8,
dma16);
err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
dma16, SB_HW_CS5530, &chip->sb);
if (err < 0) {
printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
snd_cs5530_free(chip);
return err;
}
err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
if (err < 0) {
printk(KERN_ERR "CS5530: Could not create PCM\n");
snd_cs5530_free(chip);
return err;
}
err = snd_sbmixer_new(chip->sb);
if (err < 0) {
printk(KERN_ERR "CS5530: Could not create Mixer\n");
snd_cs5530_free(chip);
return err;
}
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
snd_cs5530_free(chip);
return err;
}
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
return 0;
}
static int __devinit snd_cs5530_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_cs5530 *chip = NULL;
int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
err = snd_cs5530_create(card, pci, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
strcpy(card->driver, "CS5530");
strcpy(card->shortname, "CS5530 Audio");
sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
pci_set_drvdata(pci, card);
dev++;
return 0;
}
static struct pci_driver driver = {
.name = "CS5530_Audio",
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
.remove = __devexit_p(snd_cs5530_remove),
};
static int __init alsa_card_cs5530_init(void)
{
return pci_register_driver(&driver);
}
static void __exit alsa_card_cs5530_exit(void)
{
pci_unregister_driver(&driver);
}
module_init(alsa_card_cs5530_init)
module_exit(alsa_card_cs5530_exit)

View File

@ -51,9 +51,15 @@
#define HANA_FILENAME "emu/hana.fw" #define HANA_FILENAME "emu/hana.fw"
#define DOCK_FILENAME "emu/audio_dock.fw" #define DOCK_FILENAME "emu/audio_dock.fw"
#define EMU1010B_FILENAME "emu/emu1010b.fw"
#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
MODULE_FIRMWARE(HANA_FILENAME); MODULE_FIRMWARE(HANA_FILENAME);
MODULE_FIRMWARE(DOCK_FILENAME); MODULE_FIRMWARE(DOCK_FILENAME);
MODULE_FIRMWARE(EMU1010B_FILENAME);
MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
/************************************************************************* /*************************************************************************
@ -660,10 +666,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
return err; return err;
} }
snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
#if 0
if (fw_entry->size != 0x133a4) { if (fw_entry->size != 0x133a4) {
snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
return -EINVAL; return -EINVAL;
} }
#endif
/* The FPGA is a Xilinx Spartan IIE XC2S50E */ /* The FPGA is a Xilinx Spartan IIE XC2S50E */
/* GPIO7 -> FPGA PGMN /* GPIO7 -> FPGA PGMN
@ -694,6 +702,37 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
return 0; return 0;
} }
/*
* EMU-1010 - details found out from this driver, official MS Win drivers,
* testing the card:
*
* Audigy2 (aka Alice2):
* ---------------------
* * communication over PCI
* * conversion of 32-bit data coming over EMU32 links from HANA FPGA
* to 2 x 16-bit, using internal DSP instructions
* * slave mode, clock supplied by HANA
* * linked to HANA using:
* 32 x 32-bit serial EMU32 output channels
* 16 x EMU32 input channels
* (?) x I2S I/O channels (?)
*
* FPGA (aka HANA):
* ---------------
* * provides all (?) physical inputs and outputs of the card
* (ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
* * provides clock signal for the card and Alice2
* * two crystals - for 44.1kHz and 48kHz multiples
* * provides internal routing of signal sources to signal destinations
* * inputs/outputs to Alice2 - see above
*
* Current status of the driver:
* ----------------------------
* * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
* * PCM device nb. 2:
* 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
* 16 x 32-bit capture - snd_emu10k1_capture_efx_ops
*/
static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
{ {
unsigned int i; unsigned int i;
@ -727,7 +766,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg ); snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
snd_printdd("reg1=0x%x\n",reg); snd_printdd("reg1=0x%x\n",reg);
if (reg == 0x55) { if ((reg & 0x3f) == 0x15) {
/* FPGA netlist already present so clear it */ /* FPGA netlist already present so clear it */
/* Return to programming mode */ /* Return to programming mode */
@ -735,19 +774,32 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
} }
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg ); snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
snd_printdd("reg2=0x%x\n",reg); snd_printdd("reg2=0x%x\n",reg);
if (reg == 0x55) { if ((reg & 0x3f) == 0x15) {
/* FPGA failed to return to programming mode */ /* FPGA failed to return to programming mode */
snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
return -ENODEV; return -ENODEV;
} }
snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { if (emu->card_capabilities->emu1010 == 1) {
snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
return err; snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
return err;
}
} else if (emu->card_capabilities->emu1010 == 2) {
if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
return err;
}
} else if (emu->card_capabilities->emu1010 == 3) {
if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
return err;
}
} }
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg ); snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
if (reg != 0x55) { if ((reg & 0x3f) != 0x15) {
/* FPGA failed to be programmed */ /* FPGA failed to be programmed */
snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg); snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
return -ENODEV; return -ENODEV;
@ -850,6 +902,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1); EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
snd_emu1010_fpga_link_dst_src_write(emu, snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1); EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
/* Pavel Hofman - setting defaults for 8 more capture channels
* Defaults only, users will set their own values anyways, let's
* just copy/paste.
*/
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
#endif #endif
#if 0 #if 0
/* Original */ /* Original */
@ -943,16 +1016,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
/* Return to Audio Dock programming mode */ /* Return to Audio Dock programming mode */
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { if (emu->card_capabilities->emu1010 == 1) {
return err; if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
return err;
}
} else if (emu->card_capabilities->emu1010 == 2) {
if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
return err;
}
} else if (emu->card_capabilities->emu1010 == 3) {
if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
return err;
}
} }
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 );
snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg ); snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg ); snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg); snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
if (reg != 0x55) { if ((reg & 0x3f) != 0x15) {
/* FPGA failed to be programmed */ /* FPGA failed to be programmed */
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
return 0; return 0;
@ -1227,9 +1311,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.emu10k2_chip = 1, .emu10k2_chip = 1,
.ca0108_chip = 1, .ca0108_chip = 1,
.ca_cardbus_chip = 1, .ca_cardbus_chip = 1,
.spi_dac = 1, .spk71 = 1 ,
.i2c_adc = 1, .emu1010 = 3} ,
.spk71 = 1} , {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
.driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.spk71 = 1 ,
.emu1010 = 2} ,
{.vendor = 0x1102, .device = 0x0008, {.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
.id = "Audigy2", .id = "Audigy2",
@ -1663,12 +1753,13 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->fx8010.extout_mask = extout_mask; emu->fx8010.extout_mask = extout_mask;
emu->enable_ir = enable_ir; emu->enable_ir = enable_ir;
if (emu->card_capabilities->ca_cardbus_chip) {
if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
goto error;
}
if (emu->card_capabilities->ecard) { if (emu->card_capabilities->ecard) {
if ((err = snd_emu10k1_ecard_init(emu)) < 0) if ((err = snd_emu10k1_ecard_init(emu)) < 0)
goto error; goto error;
} else if (emu->card_capabilities->ca_cardbus_chip) {
if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
goto error;
} else if (emu->card_capabilities->emu1010) { } else if (emu->card_capabilities->emu1010) {
if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
snd_emu10k1_free(emu); snd_emu10k1_free(emu);
@ -1814,10 +1905,10 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
{ {
if (emu->card_capabilities->ca_cardbus_chip)
snd_emu10k1_cardbus_init(emu);
if (emu->card_capabilities->ecard) if (emu->card_capabilities->ecard)
snd_emu10k1_ecard_init(emu); snd_emu10k1_ecard_init(emu);
else if (emu->card_capabilities->ca_cardbus_chip)
snd_emu10k1_cardbus_init(emu);
else if (emu->card_capabilities->emu1010) else if (emu->card_capabilities->emu1010)
snd_emu10k1_emu1010_init(emu); snd_emu10k1_emu1010_init(emu);
else else

View File

@ -1123,6 +1123,11 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
} }
/*
* Used for emu1010 - conversion from 32-bit capture inputs from HANA
* to 2 x 16-bit registers in audigy - their values are read via DMA.
* Conversion is performed by Audigy DSP instructions of FX8010.
*/
static int snd_emu10k1_audigy_dsp_convert_32_to_2x16( static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
struct snd_emu10k1_fx8010_code *icode, struct snd_emu10k1_fx8010_code *icode,
u32 *ptr, int tmp, int bit_shifter16, u32 *ptr, int tmp, int bit_shifter16,
@ -1193,7 +1198,11 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
#if 1 #if 1
/* PCM front Playback Volume (independent from stereo mix) */ /* PCM front Playback Volume (independent from stereo mix)
* playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
* where gpr contains attenuation from corresponding mixer control
* (snd_emu10k1_init_stereo_control)
*/
A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT)); A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT)); A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100); snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
@ -1549,7 +1558,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
if (emu->card_capabilities->emu1010) { if (emu->card_capabilities->emu1010) {
snd_printk("EMU inputs on\n"); snd_printk("EMU inputs on\n");
/* Capture 8 channels of S32_LE sound */ /* Capture 16 (originally 8) channels of S32_LE sound */
/* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */ /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */ /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
@ -1560,6 +1569,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) ); snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
/* Right ADC in 1 of 2 */ /* Right ADC in 1 of 2 */
gpr_map[gpr++] = 0x00000000; gpr_map[gpr++] = 0x00000000;
/* Delaying by one sample: instead of copying the input
* value A_P16VIN to output A_FXBUS2 as in the first channel,
* we use an auxiliary register, delaying the value by one
* sample
*/
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) ); snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000); A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000; gpr_map[gpr++] = 0x00000000;
@ -1583,6 +1597,66 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
gpr_map[gpr++] = 0x00000000; gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) ); snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000); A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
/* Pavel Hofman - we still have voices, A_FXBUS2s, and
* A_P16VINs available -
* let's add 8 more capture channels - total of 16
*/
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x10));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x12));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x14));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x16));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x18));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x1a));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x1c));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
A_C_00000000, A_C_00000000);
gpr_map[gpr++] = 0x00000000;
snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
bit_shifter16,
A_GPR(gpr - 1),
A_FXBUS2(0x1e));
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
A_C_00000000, A_C_00000000);
#if 0 #if 0
for (z = 4; z < 8; z++) { for (z = 4; z < 8; z++) {

View File

@ -77,6 +77,10 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
/*
* Items labels in enum mixer controls assigning source data to
* each destination
*/
static char *emu1010_src_texts[] = { static char *emu1010_src_texts[] = {
"Silence", "Silence",
"Dock Mic A", "Dock Mic A",
@ -133,6 +137,9 @@ static char *emu1010_src_texts[] = {
"DSP 31", "DSP 31",
}; };
/*
* List of data sources available for each destination
*/
static unsigned int emu1010_src_regs[] = { static unsigned int emu1010_src_regs[] = {
EMU_SRC_SILENCE,/* 0 */ EMU_SRC_SILENCE,/* 0 */
EMU_SRC_DOCK_MIC_A1, /* 1 */ EMU_SRC_DOCK_MIC_A1, /* 1 */
@ -189,6 +196,10 @@ static unsigned int emu1010_src_regs[] = {
EMU_SRC_ALICE_EMU32B+0xf, /* 52 */ EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
}; };
/*
* Data destinations - physical EMU outputs.
* Each destination has an enum mixer control to choose a data source
*/
static unsigned int emu1010_output_dst[] = { static unsigned int emu1010_output_dst[] = {
EMU_DST_DOCK_DAC1_LEFT1, /* 0 */ EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */ EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
@ -216,6 +227,11 @@ static unsigned int emu1010_output_dst[] = {
EMU_DST_HANA_ADAT+7, /* 23 */ EMU_DST_HANA_ADAT+7, /* 23 */
}; };
/*
* Data destinations - HANA outputs going to Alice2 (audigy) for
* capture (EMU32 + I2S links)
* Each destination has an enum mixer control to choose a data source
*/
static unsigned int emu1010_input_dst[] = { static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_0, EMU_DST_ALICE2_EMU32_0,
EMU_DST_ALICE2_EMU32_1, EMU_DST_ALICE2_EMU32_1,

View File

@ -1233,24 +1233,26 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.rate_min = runtime->hw.rate_max = 48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000;
spin_lock_irq(&emu->reg_lock); spin_lock_irq(&emu->reg_lock);
if (emu->card_capabilities->emu1010) { if (emu->card_capabilities->emu1010) {
/* TODO /* Nb. of channels has been increased to 16 */
/* TODO
* SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
* SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
* SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
* SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
* rate_min = 44100, * rate_min = 44100,
* rate_max = 192000, * rate_max = 192000,
* channels_min = 8, * channels_min = 16,
* channels_max = 8, * channels_max = 16,
* Need to add mixer control to fix sample rate * Need to add mixer control to fix sample rate
* *
* There are 16 mono channels of 16bits each. * There are 32 mono channels of 16bits each.
* 24bit Audio uses 2x channels over 16bit * 24bit Audio uses 2x channels over 16bit
* 96kHz uses 2x channels over 48kHz * 96kHz uses 2x channels over 48kHz
* 192kHz uses 4x channels over 48kHz * 192kHz uses 4x channels over 48kHz
* So, for 48kHz 24bit, one has 8 channels * So, for 48kHz 24bit, one has 16 channels
* for 96kHz 24bit, one has 4 channels * for 96kHz 24bit, one has 8 channels
* for 192kHz 24bit, one has 2 channels * for 192kHz 24bit, one has 4 channels
*
*/ */
#if 1 #if 1
switch (emu->emu1010.internal_clock) { switch (emu->emu1010.internal_clock) {
@ -1258,13 +1260,15 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
/* For 44.1kHz */ /* For 44.1kHz */
runtime->hw.rates = SNDRV_PCM_RATE_44100; runtime->hw.rates = SNDRV_PCM_RATE_44100;
runtime->hw.rate_min = runtime->hw.rate_max = 44100; runtime->hw.rate_min = runtime->hw.rate_max = 44100;
runtime->hw.channels_min = runtime->hw.channels_max = 8; runtime->hw.channels_min =
runtime->hw.channels_max = 16;
break; break;
case 1: case 1:
/* For 48kHz */ /* For 48kHz */
runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rates = SNDRV_PCM_RATE_48000;
runtime->hw.rate_min = runtime->hw.rate_max = 48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000;
runtime->hw.channels_min = runtime->hw.channels_max = 8; runtime->hw.channels_min =
runtime->hw.channels_max = 16;
break; break;
}; };
#endif #endif
@ -1282,7 +1286,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
#endif #endif
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
/* efx_voices_mask[0] is expected to be zero /* efx_voices_mask[0] is expected to be zero
* efx_voices_mask[1] is expected to have 16bits set * efx_voices_mask[1] is expected to have 32bits set
*/ */
} else { } else {
runtime->hw.channels_min = runtime->hw.channels_max = 0; runtime->hw.channels_min = runtime->hw.channels_max = 0;
@ -1787,11 +1791,24 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
if (emu->audigy) { if (emu->audigy) {
emu->efx_voices_mask[0] = 0; emu->efx_voices_mask[0] = 0;
emu->efx_voices_mask[1] = 0xffff; if (emu->card_capabilities->emu1010)
/* Pavel Hofman - 32 voices will be used for
* capture (write mode) -
* each bit = corresponding voice
*/
emu->efx_voices_mask[1] = 0xffffffff;
else
emu->efx_voices_mask[1] = 0xffff;
} else { } else {
emu->efx_voices_mask[0] = 0xffff0000; emu->efx_voices_mask[0] = 0xffff0000;
emu->efx_voices_mask[1] = 0; emu->efx_voices_mask[1] = 0;
} }
/* For emu1010, the control has to set 32 upper bits (voices)
* out of the 64 bits (voices) to true for the 16-channels capture
* to work correctly. Correct A_FXWC2 initial value (0xffffffff)
* is already defined but the snd_emu10k1_pcm_efx_voices_mask
* control can override this register's value.
*/
kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
if (!kctl) if (!kctl)
return -ENOMEM; return -ENOMEM;

View File

@ -1607,8 +1607,8 @@ struct es1371_quirk {
unsigned char rev; /* revision */ unsigned char rev; /* revision */
}; };
static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq, static int es1371_quirk_lookup(struct ensoniq *ensoniq,
struct es1371_quirk *list) struct es1371_quirk *list)
{ {
while (list->vid != (unsigned short)PCI_ANY_ID) { while (list->vid != (unsigned short)PCI_ANY_ID) {
if (ensoniq->pci->vendor == list->vid && if (ensoniq->pci->vendor == list->vid &&

View File

@ -341,6 +341,9 @@ struct azx {
unsigned int single_cmd :1; unsigned int single_cmd :1;
unsigned int polling_mode :1; unsigned int polling_mode :1;
unsigned int msi :1; unsigned int msi :1;
/* for debugging */
unsigned int last_cmd; /* last issued command (to sync) */
}; };
/* driver types */ /* driver types */
@ -466,18 +469,10 @@ static void azx_free_cmd_io(struct azx *chip)
} }
/* send a command */ /* send a command */
static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
unsigned int verb, unsigned int para)
{ {
struct azx *chip = codec->bus->private_data; struct azx *chip = codec->bus->private_data;
unsigned int wp; unsigned int wp;
u32 val;
val = (u32)(codec->addr & 0x0f) << 28;
val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
val |= para;
/* add command to corb */ /* add command to corb */
wp = azx_readb(chip, CORBWP); wp = azx_readb(chip, CORBWP);
@ -538,12 +533,12 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
} }
if (! chip->rirb.cmds) if (! chip->rirb.cmds)
return chip->rirb.res; /* the last value */ return chip->rirb.res; /* the last value */
schedule_timeout_interruptible(1); schedule_timeout(1);
} while (time_after_eq(timeout, jiffies)); } while (time_after_eq(timeout, jiffies));
if (chip->msi) { if (chip->msi) {
snd_printk(KERN_WARNING "hda_intel: No response from codec, " snd_printk(KERN_WARNING "hda_intel: No response from codec, "
"disabling MSI...\n"); "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
free_irq(chip->irq, chip); free_irq(chip->irq, chip);
chip->irq = -1; chip->irq = -1;
pci_disable_msi(chip->pci); pci_disable_msi(chip->pci);
@ -555,13 +550,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
if (!chip->polling_mode) { if (!chip->polling_mode) {
snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, " snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
"switching to polling mode...\n"); "switching to polling mode: last cmd=0x%08x\n",
chip->last_cmd);
chip->polling_mode = 1; chip->polling_mode = 1;
goto again; goto again;
} }
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode...\n"); "switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd);
chip->rirb.rp = azx_readb(chip, RIRBWP); chip->rirb.rp = azx_readb(chip, RIRBWP);
chip->rirb.cmds = 0; chip->rirb.cmds = 0;
/* switch to single_cmd mode */ /* switch to single_cmd mode */
@ -581,20 +578,11 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
*/ */
/* send a command */ /* send a command */
static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid, static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
int direct, unsigned int verb,
unsigned int para)
{ {
struct azx *chip = codec->bus->private_data; struct azx *chip = codec->bus->private_data;
u32 val;
int timeout = 50; int timeout = 50;
val = (u32)(codec->addr & 0x0f) << 28;
val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
val |= para;
while (timeout--) { while (timeout--) {
/* check ICB busy bit */ /* check ICB busy bit */
if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) { if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
@ -639,10 +627,19 @@ static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
unsigned int para) unsigned int para)
{ {
struct azx *chip = codec->bus->private_data; struct azx *chip = codec->bus->private_data;
u32 val;
val = (u32)(codec->addr & 0x0f) << 28;
val |= (u32)direct << 27;
val |= (u32)nid << 20;
val |= verb << 8;
val |= para;
chip->last_cmd = val;
if (chip->single_cmd) if (chip->single_cmd)
return azx_single_send_cmd(codec, nid, direct, verb, para); return azx_single_send_cmd(codec, val);
else else
return azx_corb_send_cmd(codec, nid, direct, verb, para); return azx_corb_send_cmd(codec, val);
} }
/* get a response */ /* get a response */
@ -1788,6 +1785,12 @@ static struct pci_device_id azx_ids[] = {
{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
{ 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
{ 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
{ 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
{ 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
{ 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
{ 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
{ 0, } { 0, }
}; };
MODULE_DEVICE_TABLE(pci, azx_ids); MODULE_DEVICE_TABLE(pci, azx_ids);

View File

@ -250,6 +250,12 @@ static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffe
snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
if (codec->mfg)
snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
else
snd_iprintf(buffer, "No Modem Function Group found\n");
if (! codec->afg) if (! codec->afg)
return; return;
snd_iprintf(buffer, "Default PCM:\n"); snd_iprintf(buffer, "Default PCM:\n");

View File

@ -1,7 +1,8 @@
/* /*
* HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988 * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
* AD1986A, AD1988
* *
* Copyright (c) 2005 Takashi Iwai <tiwai@suse.de> * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
* *
* This driver is free software; you can redistribute it and/or modify * This driver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -61,7 +62,7 @@ struct ad198x_spec {
int num_channel_mode; int num_channel_mode;
/* PCM information */ /* PCM information */
struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
struct mutex amp_mutex; /* PCM volume/mute control mutex */ struct mutex amp_mutex; /* PCM volume/mute control mutex */
unsigned int spdif_route; unsigned int spdif_route;
@ -2774,12 +2775,635 @@ static int patch_ad1988(struct hda_codec *codec)
} }
/*
* AD1884 / AD1984
*
* port-B - front line/mic-in
* port-E - aux in/out
* port-F - aux in/out
* port-C - rear line/mic-in
* port-D - rear line/hp-out
* port-A - front line/hp-out
*
* AD1984 = AD1884 + two digital mic-ins
*
* FIXME:
* For simplicity, we share the single DAC for both HP and line-outs
* right now. The inidividual playbacks could be easily implemented,
* but no build-up framework is given, so far.
*/
static hda_nid_t ad1884_dac_nids[1] = {
0x04,
};
static hda_nid_t ad1884_adc_nids[2] = {
0x08, 0x09,
};
static hda_nid_t ad1884_capsrc_nids[2] = {
0x0c, 0x0d,
};
#define AD1884_SPDIF_OUT 0x02
static struct hda_input_mux ad1884_capture_source = {
.num_items = 4,
.items = {
{ "Front Mic", 0x0 },
{ "Mic", 0x1 },
{ "CD", 0x2 },
{ "Mix", 0x3 },
},
};
static struct snd_kcontrol_new ad1884_base_mixers[] = {
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
/*
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
*/
HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
* FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
.count = 2,
.info = ad198x_mux_enum_info,
.get = ad198x_mux_enum_get,
.put = ad198x_mux_enum_put,
},
/* SPDIF controls */
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
/* identical with ad1983 */
.info = ad1983_spdif_route_info,
.get = ad1983_spdif_route_get,
.put = ad1983_spdif_route_put,
},
{ } /* end */
};
static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
HDA_INPUT),
HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
HDA_INPUT),
{ } /* end */
};
/*
* initialization verbs
*/
static struct hda_verb ad1884_init_verbs[] = {
/* DACs; mute as default */
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Port-A (HP) mixer */
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Port-A pin */
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* HP selector - select DAC2 */
{0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
/* Port-D (Line-out) mixer */
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Port-D pin */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Mono-out mixer */
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Mono-out pin */
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Mono selector */
{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
/* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Port-C (rear mic) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog mixer; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
/* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
/* SPDIF output selector */
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
{ } /* end */
};
static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
spec->multiout.dac_nids = ad1884_dac_nids;
spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
spec->adc_nids = ad1884_adc_nids;
spec->capsrc_nids = ad1884_capsrc_nids;
spec->input_mux = &ad1884_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1884_base_mixers;
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1884_init_verbs;
spec->spdif_route = 0;
codec->patch_ops = ad198x_patch_ops;
return 0;
}
/*
* Lenovo Thinkpad T61/X61
*/
static struct hda_input_mux ad1984_thinkpad_capture_source = {
.num_items = 3,
.items = {
{ "Mic", 0x0 },
{ "Internal Mic", 0x1 },
{ "Mix", 0x3 },
},
};
static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
* FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
.count = 2,
.info = ad198x_mux_enum_info,
.get = ad198x_mux_enum_get,
.put = ad198x_mux_enum_put,
},
{ } /* end */
};
/* additional verbs */
static struct hda_verb ad1984_thinkpad_init_verbs[] = {
/* Port-E (docking station mic) pin */
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* docking mic boost */
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog mixer - docking mic; mute as default */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
/* enable EAPD bit */
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
{ } /* end */
};
/* Digial MIC ADC NID 0x05 + 0x06 */
static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
stream_tag, 0, format);
return 0;
}
static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
0, 0, 0);
return 0;
}
static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
.substreams = 2,
.channels_min = 2,
.channels_max = 2,
.nid = 0x05,
.ops = {
.prepare = ad1984_pcm_dmic_prepare,
.cleanup = ad1984_pcm_dmic_cleanup
},
};
static int ad1984_build_pcms(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
struct hda_pcm *info;
int err;
err = ad198x_build_pcms(codec);
if (err < 0)
return err;
info = spec->pcm_rec + codec->num_pcms;
codec->num_pcms++;
info->name = "AD1984 Digital Mic";
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
return 0;
}
/* models */
enum {
AD1984_BASIC,
AD1984_THINKPAD,
AD1984_MODELS
};
static const char *ad1984_models[AD1984_MODELS] = {
[AD1984_BASIC] = "basic",
[AD1984_THINKPAD] = "thinkpad",
};
static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
{}
};
static int patch_ad1984(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int board_config, err;
err = patch_ad1884(codec);
if (err < 0)
return err;
spec = codec->spec;
board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
ad1984_models, ad1984_cfg_tbl);
switch (board_config) {
case AD1984_BASIC:
/* additional digital mics */
spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
codec->patch_ops.build_pcms = ad1984_build_pcms;
break;
case AD1984_THINKPAD:
spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1984_thinkpad_capture_source;
spec->mixers[0] = ad1984_thinkpad_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
break;
}
return 0;
}
/*
* AD1882
*
* port-A - front hp-out
* port-B - front mic-in
* port-C - rear line-in, shared surr-out (3stack)
* port-D - rear line-out
* port-E - rear mic-in, shared clfe-out (3stack)
* port-F - rear surr-out (6stack)
* port-G - rear clfe-out (6stack)
*/
static hda_nid_t ad1882_dac_nids[3] = {
0x04, 0x03, 0x05
};
static hda_nid_t ad1882_adc_nids[2] = {
0x08, 0x09,
};
static hda_nid_t ad1882_capsrc_nids[2] = {
0x0c, 0x0d,
};
#define AD1882_SPDIF_OUT 0x02
/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
static struct hda_input_mux ad1882_capture_source = {
.num_items = 5,
.items = {
{ "Front Mic", 0x1 },
{ "Mic", 0x4 },
{ "Line", 0x2 },
{ "CD", 0x3 },
{ "Mix", 0x7 },
},
};
static struct snd_kcontrol_new ad1882_base_mixers[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
* FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
.count = 2,
.info = ad198x_mux_enum_info,
.get = ad198x_mux_enum_get,
.put = ad198x_mux_enum_put,
},
/* SPDIF controls */
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
/* identical with ad1983 */
.info = ad1983_spdif_route_info,
.get = ad1983_spdif_route_get,
.put = ad1983_spdif_route_put,
},
{ } /* end */
};
static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
.info = ad198x_ch_mode_info,
.get = ad198x_ch_mode_get,
.put = ad198x_ch_mode_put,
},
{ } /* end */
};
static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
{ } /* end */
};
static struct hda_verb ad1882_ch2_init[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{ } /* end */
};
static struct hda_verb ad1882_ch4_init[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{ } /* end */
};
static struct hda_verb ad1882_ch6_init[] = {
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{ } /* end */
};
static struct hda_channel_mode ad1882_modes[3] = {
{ 2, ad1882_ch2_init },
{ 4, ad1882_ch4_init },
{ 6, ad1882_ch6_init },
};
/*
* initialization verbs
*/
static struct hda_verb ad1882_init_verbs[] = {
/* DACs; mute as default */
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
/* Port-A (HP) mixer */
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Port-A pin */
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* HP selector - select DAC2 */
{0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
/* Port-D (Line-out) mixer */
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Port-D pin */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Mono-out mixer */
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
/* Mono-out pin */
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Port-B (front mic) pin */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
/* Port-C (line-in) pin */
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
/* Port-C mixer - mute as input */
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
/* Port-E (mic-in) pin */
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
/* Port-E mixer - mute as input */
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
/* Port-F (surround) */
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Port-G (CLFE) */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
/* Analog mixer; mute as default */
/* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
/* Analog Mix output amp */
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
/* SPDIF output selector */
{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
{ } /* end */
};
/* models */
enum {
AD1882_3STACK,
AD1882_6STACK,
AD1882_MODELS
};
static const char *ad1882_models[AD1986A_MODELS] = {
[AD1882_3STACK] = "3stack",
[AD1882_6STACK] = "6stack",
};
static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int board_config;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
return -ENOMEM;
mutex_init(&spec->amp_mutex);
codec->spec = spec;
spec->multiout.max_channels = 6;
spec->multiout.num_dacs = 3;
spec->multiout.dac_nids = ad1882_dac_nids;
spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
spec->adc_nids = ad1882_adc_nids;
spec->capsrc_nids = ad1882_capsrc_nids;
spec->input_mux = &ad1882_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1882_base_mixers;
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1882_init_verbs;
spec->spdif_route = 0;
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
ad1882_models, NULL);
switch (board_config) {
default:
case AD1882_3STACK:
spec->num_mixers = 2;
spec->mixers[1] = ad1882_3stack_mixers;
spec->channel_mode = ad1882_modes;
spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
spec->need_dac_fix = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
break;
case AD1882_6STACK:
spec->num_mixers = 2;
spec->mixers[1] = ad1882_6stack_mixers;
break;
}
return 0;
}
/* /*
* patch entries * patch entries
*/ */
struct hda_codec_preset snd_hda_preset_analog[] = { struct hda_codec_preset snd_hda_preset_analog[] = {
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },

View File

@ -172,6 +172,7 @@ static int patch_atihdmi(struct hda_codec *codec)
*/ */
struct hda_codec_preset snd_hda_preset_atihdmi[] = { struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
{} /* terminator */ {} /* terminator */

View File

@ -801,7 +801,9 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP), SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
{} {}
}; };

File diff suppressed because it is too large Load Diff

View File

@ -304,8 +304,12 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
/* VIA HDA on Clevo m540 */
{ .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
/* Asus A8J Modem (SM56) */ /* Asus A8J Modem (SM56) */
{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
/* LG LW20 modem */
{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
{} {}
}; };

View File

@ -44,6 +44,7 @@ enum {
enum { enum {
STAC_9205_REF, STAC_9205_REF,
STAC_M43xx,
STAC_9205_MODELS STAC_9205_MODELS
}; };
@ -59,11 +60,19 @@ enum {
STAC_D945_REF, STAC_D945_REF,
STAC_D945GTP3, STAC_D945GTP3,
STAC_D945GTP5, STAC_D945GTP5,
STAC_922X_DELL,
STAC_INTEL_MAC_V1,
STAC_INTEL_MAC_V2,
STAC_INTEL_MAC_V3,
STAC_INTEL_MAC_V4,
STAC_INTEL_MAC_V5,
/* for backward compitability */
STAC_MACMINI, STAC_MACMINI,
STAC_MACBOOK, STAC_MACBOOK,
STAC_MACBOOK_PRO_V1, STAC_MACBOOK_PRO_V1,
STAC_MACBOOK_PRO_V2, STAC_MACBOOK_PRO_V2,
STAC_IMAC_INTEL, STAC_IMAC_INTEL,
STAC_IMAC_INTEL_20,
STAC_922X_MODELS STAC_922X_MODELS
}; };
@ -210,7 +219,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x14, 0x16, 0x17, 0x18, 0x0f, 0x14, 0x16, 0x17, 0x18,
0x21, 0x22, 0x21, 0x22,
}; };
static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
@ -326,8 +334,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
}; };
static struct snd_kcontrol_new stac925x_mixer[] = { static struct snd_kcontrol_new stac925x_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
{ {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Source", .name = "Input Source",
@ -549,44 +555,78 @@ static unsigned int d945gtp5_pin_configs[10] = {
0x02a19320, 0x40000100, 0x02a19320, 0x40000100,
}; };
static unsigned int macbook_pro_v1_pin_configs[10] = { static unsigned int intel_mac_v1_pin_configs[10] = {
0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010, 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e, 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
0x02a19320, 0x400000fb
};
static unsigned int macbook_pro_v2_pin_configs[10] = {
0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
0x400000fc, 0x400000fb, 0x400000fc, 0x400000fb,
}; };
static unsigned int imac_intel_pin_configs[10] = { static unsigned int intel_mac_v2_pin_configs[10] = {
0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe, 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa, 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
0x400000fc, 0x400000fb, 0x400000fc, 0x400000fb,
}; };
static unsigned int intel_mac_v3_pin_configs[10] = {
0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
0x400000fc, 0x400000fb,
};
static unsigned int intel_mac_v4_pin_configs[10] = {
0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
0x400000fc, 0x400000fb,
};
static unsigned int intel_mac_v5_pin_configs[10] = {
0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
0x400000fc, 0x400000fb,
};
static unsigned int stac922x_dell_pin_configs[10] = {
0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
0x50a003f3, 0x405003f4
};
static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
[STAC_D945_REF] = ref922x_pin_configs, [STAC_D945_REF] = ref922x_pin_configs,
[STAC_D945GTP3] = d945gtp3_pin_configs, [STAC_D945GTP3] = d945gtp3_pin_configs,
[STAC_D945GTP5] = d945gtp5_pin_configs, [STAC_D945GTP5] = d945gtp5_pin_configs,
[STAC_MACMINI] = macbook_pro_v1_pin_configs, [STAC_922X_DELL] = stac922x_dell_pin_configs,
[STAC_MACBOOK] = macbook_pro_v1_pin_configs, [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
[STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs, [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
[STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs, [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
[STAC_IMAC_INTEL] = imac_intel_pin_configs, [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
[STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
/* for backward compitability */
[STAC_MACMINI] = intel_mac_v3_pin_configs,
[STAC_MACBOOK] = intel_mac_v5_pin_configs,
[STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
[STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
[STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
[STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
}; };
static const char *stac922x_models[STAC_922X_MODELS] = { static const char *stac922x_models[STAC_922X_MODELS] = {
[STAC_D945_REF] = "ref", [STAC_D945_REF] = "ref",
[STAC_D945GTP5] = "5stack", [STAC_D945GTP5] = "5stack",
[STAC_D945GTP3] = "3stack", [STAC_D945GTP3] = "3stack",
[STAC_922X_DELL] = "dell",
[STAC_INTEL_MAC_V1] = "intel-mac-v1",
[STAC_INTEL_MAC_V2] = "intel-mac-v2",
[STAC_INTEL_MAC_V3] = "intel-mac-v3",
[STAC_INTEL_MAC_V4] = "intel-mac-v4",
[STAC_INTEL_MAC_V5] = "intel-mac-v5",
/* for backward compitability */
[STAC_MACMINI] = "macmini", [STAC_MACMINI] = "macmini",
[STAC_MACBOOK] = "macbook", [STAC_MACBOOK] = "macbook",
[STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
[STAC_MACBOOK_PRO_V2] = "macbook-pro", [STAC_MACBOOK_PRO_V2] = "macbook-pro",
[STAC_IMAC_INTEL] = "imac-intel", [STAC_IMAC_INTEL] = "imac-intel",
[STAC_IMAC_INTEL_20] = "imac-intel-20",
}; };
static struct snd_pci_quirk stac922x_cfg_tbl[] = { static struct snd_pci_quirk stac922x_cfg_tbl[] = {
@ -649,7 +689,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
/* other systems */ /* other systems */
/* Apple Mac Mini (early 2006) */ /* Apple Mac Mini (early 2006) */
SND_PCI_QUIRK(0x8384, 0x7680, SND_PCI_QUIRK(0x8384, 0x7680,
"Mac Mini", STAC_MACMINI), "Mac Mini", STAC_INTEL_MAC_V3),
/* Dell */
SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL),
{} /* terminator */ {} /* terminator */
}; };
@ -730,7 +773,8 @@ static unsigned int ref9205_pin_configs[12] = {
}; };
static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
ref9205_pin_configs, [STAC_REF] = ref9205_pin_configs,
[STAC_M43xx] = NULL,
}; };
static const char *stac9205_models[STAC_9205_MODELS] = { static const char *stac9205_models[STAC_9205_MODELS] = {
@ -741,6 +785,10 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
/* SigmaTel reference board */ /* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF), "DFI LanParty", STAC_9205_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01f8,
"Dell Precision", STAC_M43xx),
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01ff,
"Dell Precision", STAC_M43xx),
{} /* terminator */ {} /* terminator */
}; };
@ -770,33 +818,56 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
return 0; return 0;
} }
static void stac92xx_set_config_reg(struct hda_codec *codec,
hda_nid_t pin_nid, unsigned int pin_config)
{
int i;
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
pin_config & 0x000000ff);
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
(pin_config & 0x0000ff00) >> 8);
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
(pin_config & 0x00ff0000) >> 16);
snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
pin_config >> 24);
i = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_CONFIG_DEFAULT,
0x00);
snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
pin_nid, i);
}
static void stac92xx_set_config_regs(struct hda_codec *codec) static void stac92xx_set_config_regs(struct hda_codec *codec)
{ {
int i; int i;
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
unsigned int pin_cfg;
if (! spec->pin_nids || ! spec->pin_configs) if (!spec->pin_configs)
return; return;
for (i = 0; i < spec->num_pins; i++) { for (i = 0; i < spec->num_pins; i++)
snd_hda_codec_write(codec, spec->pin_nids[i], 0, stac92xx_set_config_reg(codec, spec->pin_nids[i],
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, spec->pin_configs[i]);
spec->pin_configs[i] & 0x000000ff); }
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, static void stac92xx_enable_gpio_mask(struct hda_codec *codec,
(spec->pin_configs[i] & 0x0000ff00) >> 8); int gpio_mask, int gpio_data)
snd_hda_codec_write(codec, spec->pin_nids[i], 0, {
AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, /* Configure GPIOx as output */
(spec->pin_configs[i] & 0x00ff0000) >> 16); snd_hda_codec_write(codec, codec->afg, 0,
snd_hda_codec_write(codec, spec->pin_nids[i], 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mask);
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, /* Configure GPIOx as CMOS */
spec->pin_configs[i] >> 24); snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0, /* Assert GPIOx */
AC_VERB_GET_CONFIG_DEFAULT, snd_hda_codec_write(codec, codec->afg, 0,
0x00); AC_VERB_SET_GPIO_DATA, gpio_data);
snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg); /* Enable GPIOx */
} snd_hda_codec_write(codec, codec->afg, 0,
AC_VERB_SET_GPIO_MASK, gpio_mask);
} }
/* /*
@ -1168,7 +1239,7 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
* and 9202/925x. For those, dac_nids[] must be hard-coded. * and 9202/925x. For those, dac_nids[] must be hard-coded.
*/ */
static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
const struct auto_pin_cfg *cfg) struct auto_pin_cfg *cfg)
{ {
struct sigmatel_spec *spec = codec->spec; struct sigmatel_spec *spec = codec->spec;
int i, j, conn_len = 0; int i, j, conn_len = 0;
@ -1193,6 +1264,13 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
} }
if (j == conn_len) { if (j == conn_len) {
if (spec->multiout.num_dacs > 0) {
/* we have already working output pins,
* so let's drop the broken ones again
*/
cfg->line_outs = spec->multiout.num_dacs;
break;
}
/* error out, no available DAC found */ /* error out, no available DAC found */
snd_printk(KERN_ERR snd_printk(KERN_ERR
"%s: No available DAC for pin 0x%x\n", "%s: No available DAC for pin 0x%x\n",
@ -1334,7 +1412,15 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
continue; continue;
add_spec_dacs(spec, nid); add_spec_dacs(spec, nid);
} }
for (i = 0; i < cfg->line_outs; i++) {
nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
if (check_in_dac_nids(spec, nid))
nid = 0;
if (! nid)
continue;
add_spec_dacs(spec, nid);
}
for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) { for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
static const char *pfxs[] = { static const char *pfxs[] = {
"Speaker", "External Speaker", "Speaker2", "Speaker", "External Speaker", "Speaker2",
@ -1891,7 +1977,7 @@ static int patch_stac9200(struct hda_codec *codec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
spec->num_pins = 8; spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
spec->pin_nids = stac9200_pin_nids; spec->pin_nids = stac9200_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
stac9200_models, stac9200_models,
@ -1941,7 +2027,7 @@ static int patch_stac925x(struct hda_codec *codec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
spec->num_pins = 8; spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
spec->pin_nids = stac925x_pin_nids; spec->pin_nids = stac925x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS, spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
stac925x_models, stac925x_models,
@ -2013,29 +2099,41 @@ static int patch_stac922x(struct hda_codec *codec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
spec->num_pins = 10; spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
spec->pin_nids = stac922x_pin_nids; spec->pin_nids = stac922x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
stac922x_models, stac922x_models,
stac922x_cfg_tbl); stac922x_cfg_tbl);
if (spec->board_config == STAC_MACMINI) { if (spec->board_config == STAC_INTEL_MAC_V3) {
spec->gpio_mute = 1; spec->gpio_mute = 1;
/* Intel Macs have all same PCI SSID, so we need to check /* Intel Macs have all same PCI SSID, so we need to check
* codec SSID to distinguish the exact models * codec SSID to distinguish the exact models
*/ */
printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
switch (codec->subsystem_id) { switch (codec->subsystem_id) {
case 0x106b0a00: /* MacBook First generatoin */
spec->board_config = STAC_MACBOOK; case 0x106b0800:
spec->board_config = STAC_INTEL_MAC_V1;
break; break;
case 0x106b0200: /* MacBook Pro first generation */ case 0x106b0600:
spec->board_config = STAC_MACBOOK_PRO_V1; case 0x106b0700:
spec->board_config = STAC_INTEL_MAC_V2;
break; break;
case 0x106b1e00: /* MacBook Pro second generation */ case 0x106b0e00:
spec->board_config = STAC_MACBOOK_PRO_V2; case 0x106b0f00:
case 0x106b1600:
case 0x106b1700:
case 0x106b0200:
case 0x106b1e00:
spec->board_config = STAC_INTEL_MAC_V3;
break; break;
case 0x106b0700: /* Intel-based iMac */ case 0x106b1a00:
spec->board_config = STAC_IMAC_INTEL; case 0x00000100:
spec->board_config = STAC_INTEL_MAC_V4;
break;
case 0x106b0a00:
case 0x106b2200:
spec->board_config = STAC_INTEL_MAC_V5;
break; break;
} }
} }
@ -2082,6 +2180,13 @@ static int patch_stac922x(struct hda_codec *codec)
codec->patch_ops = stac92xx_patch_ops; codec->patch_ops = stac92xx_patch_ops;
/* Fix Mux capture level; max to 2 */
snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
(0 << AC_AMPCAP_OFFSET_SHIFT) |
(2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(0 << AC_AMPCAP_MUTE_SHIFT));
return 0; return 0;
} }
@ -2095,7 +2200,7 @@ static int patch_stac927x(struct hda_codec *codec)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
spec->num_pins = 14; spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
spec->pin_nids = stac927x_pin_nids; spec->pin_nids = stac927x_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
stac927x_models, stac927x_models,
@ -2141,7 +2246,9 @@ static int patch_stac927x(struct hda_codec *codec)
} }
spec->multiout.dac_nids = spec->dac_nids; spec->multiout.dac_nids = spec->dac_nids;
/* GPIO0 High = Enable EAPD */
stac92xx_enable_gpio_mask(codec, 0x00000001, 0x00000001);
err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
if (!err) { if (!err) {
if (spec->board_config < 0) { if (spec->board_config < 0) {
@ -2159,27 +2266,20 @@ static int patch_stac927x(struct hda_codec *codec)
codec->patch_ops = stac92xx_patch_ops; codec->patch_ops = stac92xx_patch_ops;
/* Fix Mux capture level; max to 2 */
snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
(0 << AC_AMPCAP_OFFSET_SHIFT) |
(2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(0 << AC_AMPCAP_MUTE_SHIFT));
return 0; return 0;
} }
static int patch_stac9205(struct hda_codec *codec) static int patch_stac9205(struct hda_codec *codec)
{ {
struct sigmatel_spec *spec; struct sigmatel_spec *spec;
int err; int err, gpio_mask, gpio_data;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL) if (spec == NULL)
return -ENOMEM; return -ENOMEM;
codec->spec = spec; codec->spec = spec;
spec->num_pins = 14; spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
spec->pin_nids = stac9205_pin_nids; spec->pin_nids = stac9205_pin_nids;
spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
stac9205_models, stac9205_models,
@ -2209,19 +2309,21 @@ static int patch_stac9205(struct hda_codec *codec)
spec->mixer = stac9205_mixer; spec->mixer = stac9205_mixer;
spec->multiout.dac_nids = spec->dac_nids; spec->multiout.dac_nids = spec->dac_nids;
if (spec->board_config == STAC_M43xx) {
/* Enable SPDIF in/out */
stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
/* Configure GPIO0 as EAPD output */ gpio_mask = 0x00000007; /* GPIO0-2 */
snd_hda_codec_write(codec, codec->afg, 0, /* GPIO0 High = EAPD, GPIO1 Low = DRM,
AC_VERB_SET_GPIO_DIRECTION, 0x00000001); * GPIO2 High = Headphone Mute
/* Configure GPIO0 as CMOS */ */
snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000); gpio_data = 0x00000005;
/* Assert GPIO0 high */ } else
snd_hda_codec_write(codec, codec->afg, 0, gpio_mask = gpio_data = 0x00000001; /* GPIO0 High = EAPD */
AC_VERB_SET_GPIO_DATA, 0x00000001);
/* Enable GPIO0 */
snd_hda_codec_write(codec, codec->afg, 0,
AC_VERB_SET_GPIO_MASK, 0x00000001);
stac92xx_enable_gpio_mask(codec, gpio_mask, gpio_data);
err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
if (!err) { if (!err) {
if (spec->board_config < 0) { if (spec->board_config < 0) {
@ -2256,8 +2358,8 @@ static struct hda_input_mux vaio_mux = {
.num_items = 2, .num_items = 2,
.items = { .items = {
/* { "HP", 0x0 }, */ /* { "HP", 0x0 }, */
{ "Line", 0x1 }, { "Mic Jack", 0x1 },
{ "Mic", 0x2 }, { "Internal Mic", 0x2 },
{ "PCM", 0x3 }, { "PCM", 0x3 },
} }
}; };
@ -2268,7 +2370,7 @@ static struct hda_verb vaio_init[] = {
{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
@ -2284,7 +2386,7 @@ static struct hda_verb vaio_ar_init[] = {
{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */ /* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */ /* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */

View File

@ -186,7 +186,12 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch } #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
static const struct snd_akm4xxx_dac_channel revo71_front[] = { static const struct snd_akm4xxx_dac_channel revo71_front[] = {
AK_DAC("PCM Playback Volume", 2) {
.name = "PCM Playback Volume",
.num_channels = 2,
/* front channels DAC supports muting */
.switch_name = "PCM Playback Switch",
},
}; };
static const struct snd_akm4xxx_dac_channel revo71_surround[] = { static const struct snd_akm4xxx_dac_channel revo71_surround[] = {

View File

@ -1533,7 +1533,8 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
printk(KERN_ERR " force the driver to load by " printk(KERN_ERR " force the driver to load by "
"passing in the module parameter\n"); "passing in the module parameter\n");
printk(KERN_ERR " force_ac97=1\n"); printk(KERN_ERR " force_ac97=1\n");
printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); printk(KERN_ERR " or try sb16, opl3sa2, or "
"cs423x drivers instead.\n");
err = -ENXIO; err = -ENXIO;
goto __error; goto __error;
} }

View File

@ -406,7 +406,7 @@ static snd_pcm_uframes_t rme9652_hw_pointer(struct snd_rme9652 *rme9652)
} else if (!frag) } else if (!frag)
return 0; return 0;
offset -= rme9652->max_jitter; offset -= rme9652->max_jitter;
if (offset < 0) if ((int)offset < 0)
offset += period_size * 2; offset += period_size * 2;
} else { } else {
if (offset > period_size + rme9652->max_jitter) { if (offset > period_size + rme9652->max_jitter) {

View File

@ -2098,7 +2098,7 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
break; break;
schedule_timeout_uninterruptible(1); schedule_timeout(1);
} while (time_before(jiffies, end_time)); } while (time_before(jiffies, end_time));
if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@ -2117,7 +2117,7 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
chip->ac97_secondary = 1; chip->ac97_secondary = 1;
goto __ac97_ok2; goto __ac97_ok2;
} }
schedule_timeout_interruptible(1); schedule_timeout(1);
} while (time_before(jiffies, end_time)); } while (time_before(jiffies, end_time));
/* This is ok, the most of motherboards have only one codec */ /* This is ok, the most of motherboards have only one codec */

View File

@ -983,7 +983,7 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
break; break;
schedule_timeout_uninterruptible(1); schedule_timeout(1);
} while (time_before(jiffies, end_time)); } while (time_before(jiffies, end_time));
if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@ -1001,7 +1001,7 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
chip->ac97_secondary = 1; chip->ac97_secondary = 1;
goto __ac97_ok2; goto __ac97_ok2;
} }
schedule_timeout_interruptible(1); schedule_timeout(1);
} while (time_before(jiffies, end_time)); } while (time_before(jiffies, end_time));
/* This is ok, the most of motherboards have only one codec */ /* This is ok, the most of motherboards have only one codec */

View File

@ -33,3 +33,23 @@ config SND_POWERMAC_AUTO_DRC
option. option.
endmenu endmenu
menu "ALSA PowerPC devices"
depends on SND!=n && ( PPC64 || PPC32 )
config SND_PS3
tristate "PS3 Audio support"
depends on SND && PS3_PS3AV
select SND_PCM
default m
help
Say Y here to include support for audio on the PS3
To compile this driver as a module, choose M here: the module
will be called snd_ps3.
config SND_PS3_DEFAULT_START_DELAY
int "Startup delay time in ms"
depends on SND_PS3
default "2000"
endmenu

View File

@ -6,4 +6,5 @@
snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
obj-$(CONFIG_SND_PS3) += snd_ps3.o

1125
sound/ppc/snd_ps3.c Normal file

File diff suppressed because it is too large Load Diff

135
sound/ppc/snd_ps3.h Normal file
View File

@ -0,0 +1,135 @@
/*
* Audio support for PS3
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* All rights reserved.
* Copyright 2006, 2007 Sony Corporation
*
* 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; version 2 of the Licence.
*
* 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
*/
#if !defined(_SND_PS3_H_)
#define _SND_PS3_H_
#include <linux/irqreturn.h>
#define SND_PS3_DRIVER_NAME "snd_ps3"
enum snd_ps3_out_channel {
SND_PS3_OUT_SPDIF_0,
SND_PS3_OUT_SPDIF_1,
SND_PS3_OUT_SERIAL_0,
SND_PS3_OUT_DEVS
};
enum snd_ps3_dma_filltype {
SND_PS3_DMA_FILLTYPE_FIRSTFILL,
SND_PS3_DMA_FILLTYPE_RUNNING,
SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL,
SND_PS3_DMA_FILLTYPE_SILENT_RUNNING
};
enum snd_ps3_ch {
SND_PS3_CH_L = 0,
SND_PS3_CH_R = 1,
SND_PS3_CH_MAX = 2
};
struct snd_ps3_avsetting_info {
uint32_t avs_audio_ch; /* fixed */
uint32_t avs_audio_rate;
uint32_t avs_audio_width;
uint32_t avs_audio_format; /* fixed */
uint32_t avs_audio_source; /* fixed */
};
/*
* PS3 audio 'card' instance
* there should be only ONE hardware.
*/
struct snd_ps3_card_info {
struct ps3_system_bus_device *ps3_dev;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
/* hvc info */
u64 audio_lpar_addr;
u64 audio_lpar_size;
/* registers */
void __iomem *mapped_mmio_vaddr;
/* irq */
u64 audio_irq_outlet;
unsigned int irq_no;
/* remember avsetting */
struct snd_ps3_avsetting_info avs;
/* dma buffer management */
spinlock_t dma_lock;
/* dma_lock start */
void * dma_start_vaddr[2]; /* 0 for L, 1 for R */
dma_addr_t dma_start_bus_addr[2];
size_t dma_buffer_size;
void * dma_last_transfer_vaddr[2];
void * dma_next_transfer_vaddr[2];
int silent;
/* dma_lock end */
int running;
/* null buffer */
void *null_buffer_start_vaddr;
dma_addr_t null_buffer_start_dma_addr;
/* start delay */
unsigned int start_delay;
};
/* PS3 audio DMAC block size in bytes */
#define PS3_AUDIO_DMAC_BLOCK_SIZE (128)
/* one stage (stereo) of audio FIFO in bytes */
#define PS3_AUDIO_FIFO_STAGE_SIZE (256)
/* how many stages the fifo have */
#define PS3_AUDIO_FIFO_STAGE_COUNT (8)
/* fifo size 128 bytes * 8 stages * stereo (2ch) */
#define PS3_AUDIO_FIFO_SIZE \
(PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT)
/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/
#define PS3_AUDIO_DMAC_MAX_BLOCKS (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1)
#define PS3_AUDIO_NORMAL_DMA_START_CH (0)
#define PS3_AUDIO_NORMAL_DMA_COUNT (8)
#define PS3_AUDIO_NULL_DMA_START_CH \
(PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT)
#define PS3_AUDIO_NULL_DMA_COUNT (2)
#define SND_PS3_MAX_VOL (0x0F)
#define SND_PS3_MIN_VOL (0x00)
#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL
#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL
#define SND_PS3_PCM_PREALLOC_SIZE \
(PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4)
#define SND_PS3_DMA_REGION_SIZE \
(SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE)
#define PS3_AUDIO_IOID (1UL)
#endif /* _SND_PS3_H_ */

891
sound/ppc/snd_ps3_reg.h Normal file
View File

@ -0,0 +1,891 @@
/*
* Audio support for PS3
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* Copyright 2006, 2007 Sony Corporation
* All rights reserved.
*
* 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; version 2 of the License.
*
* 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
*/
/*
* interrupt / configure registers
*/
#define PS3_AUDIO_INTR_0 (0x00000100)
#define PS3_AUDIO_INTR_EN_0 (0x00000140)
#define PS3_AUDIO_CONFIG (0x00000200)
/*
* DMAC registers
* n:0..9
*/
#define PS3_AUDIO_DMAC_REGBASE(x) (0x0000210 + 0x20 * (x))
#define PS3_AUDIO_KICK(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x00)
#define PS3_AUDIO_SOURCE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x04)
#define PS3_AUDIO_DEST(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x08)
#define PS3_AUDIO_DMASIZE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C)
/*
* mute control
*/
#define PS3_AUDIO_AX_MCTRL (0x00004000)
#define PS3_AUDIO_AX_ISBP (0x00004004)
#define PS3_AUDIO_AX_AOBP (0x00004008)
#define PS3_AUDIO_AX_IC (0x00004010)
#define PS3_AUDIO_AX_IE (0x00004014)
#define PS3_AUDIO_AX_IS (0x00004018)
/*
* three wire serial
* n:0..3
*/
#define PS3_AUDIO_AO_MCTRL (0x00006000)
#define PS3_AUDIO_AO_3WMCTRL (0x00006004)
#define PS3_AUDIO_AO_3WCTRL(n) (0x00006200 + 0x200 * (n))
/*
* S/PDIF
* n:0..1
* x:0..11
* y:0..5
*/
#define PS3_AUDIO_AO_SPD_REGBASE(n) (0x00007200 + 0x200 * (n))
#define PS3_AUDIO_AO_SPDCTRL(n) \
(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00)
#define PS3_AUDIO_AO_SPDUB(n, x) \
(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x))
#define PS3_AUDIO_AO_SPDCS(n, y) \
(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y))
/*
PS3_AUDIO_INTR_0 register tells an interrupt handler which audio
DMA channel triggered the interrupt. The interrupt status for a channel
can be cleared by writing a '1' to the corresponding bit. A new interrupt
cannot be generated until the previous interrupt has been cleared.
Note that the status reported by PS3_AUDIO_INTR_0 is independent of the
value of PS3_AUDIO_INTR_EN_0.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
#define PS3_AUDIO_INTR_0_CHAN(n) (1 << ((n) * 2))
#define PS3_AUDIO_INTR_0_CHAN9 PS3_AUDIO_INTR_0_CHAN(9)
#define PS3_AUDIO_INTR_0_CHAN8 PS3_AUDIO_INTR_0_CHAN(8)
#define PS3_AUDIO_INTR_0_CHAN7 PS3_AUDIO_INTR_0_CHAN(7)
#define PS3_AUDIO_INTR_0_CHAN6 PS3_AUDIO_INTR_0_CHAN(6)
#define PS3_AUDIO_INTR_0_CHAN5 PS3_AUDIO_INTR_0_CHAN(5)
#define PS3_AUDIO_INTR_0_CHAN4 PS3_AUDIO_INTR_0_CHAN(4)
#define PS3_AUDIO_INTR_0_CHAN3 PS3_AUDIO_INTR_0_CHAN(3)
#define PS3_AUDIO_INTR_0_CHAN2 PS3_AUDIO_INTR_0_CHAN(2)
#define PS3_AUDIO_INTR_0_CHAN1 PS3_AUDIO_INTR_0_CHAN(1)
#define PS3_AUDIO_INTR_0_CHAN0 PS3_AUDIO_INTR_0_CHAN(0)
/*
The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate
an interrupt to the PU. Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the
corresponding bit in PS3_AUDIO_INTR_0. The resulting bits are OR'd together
to generate the Audio interrupt.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
Bit assignments are same as PS3_AUDIO_INTR_0
*/
/*
PS3_AUDIO_CONFIG
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/* The CLEAR field cancels all pending transfers, and stops any running DMA
transfers. Any interrupts associated with the canceled transfers
will occur as if the transfer had finished.
Since this bit is designed to recover from DMA related issues
which are caused by unpredictable situations, it is prefered to wait
for normal DMA transfer end without using this bit.
*/
#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */
/*
PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/* 3 Wire Audio Serial Output Channel Mutes (0..3) */
#define PS3_AUDIO_AX_MCTRL_ASOMT(n) (1 << (3 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_ASO3MT (1 << 0) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_ASO2MT (1 << 1) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_ASO1MT (1 << 2) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_ASO0MT (1 << 3) /* RWIVF */
/* S/PDIF mutes (0,1)*/
#define PS3_AUDIO_AX_MCTRL_SPOMT(n) (1 << (5 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_SPO1MT (1 << 4) /* RWIVF */
#define PS3_AUDIO_AX_MCTRL_SPO0MT (1 << 5) /* RWIVF */
/* All 3 Wire Serial Outputs Mute */
#define PS3_AUDIO_AX_MCTRL_AASOMT (1 << 13) /* RWIVF */
/* All S/PDIF Mute */
#define PS3_AUDIO_AX_MCTRL_ASPOMT (1 << 14) /* RWIVF */
/* All Audio Outputs Mute */
#define PS3_AUDIO_AX_MCTRL_AAOMT (1 << 15) /* RWIVF */
/*
S/PDIF Outputs Buffer Read/Write Pointer Register
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
S/PDIF Output Channel Read Buffer Numbers
Buffer number is value of field.
Indicates current read access buffer ID from Audio Data
Transfer controller of S/PDIF Output
*/
#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */
#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK (0x7 << 0) /* R-IUF */
#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK (0x7 << 4) /* R-IUF */
/*
S/PDIF Output Channel Buffer Write Numbers
Indicates current write access buffer ID from bus master.
*/
#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 << 4 * (5 - (n))) /* R-IUF */
#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK (0x7 << 16) /* R-IUF */
#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK (0x7 << 20) /* R-IUF */
/*
3 Wire Audio Serial Outputs Buffer Read/Write
Pointer Register
Buffer number is value of field
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
3 Wire Audio Serial Output Channel Buffer Read Numbers
Indicates current read access buffer Id from Audio Data Transfer
Controller of 3 Wire Audio Serial Output Channels
*/
#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK (0x7 << 0) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK (0x7 << 4) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK (0x7 << 8) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK (0x7 << 12) /* R-IUF */
/*
3 Wire Audio Serial Output Channel Buffer Write Numbers
Indicates current write access buffer ID from bus master.
*/
#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK (0x7 << 16) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK (0x7 << 20) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK (0x7 << 24) /* R-IUF */
#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK (0x7 << 28) /* R-IUF */
/*
Audio Port Interrupt Condition Register
For the fields in this register, the following values apply:
0 = Interrupt is generated every interrupt event.
1 = Interrupt is generated every 2 interrupt events.
2 = Interrupt is generated every 4 interrupt events.
3 = Reserved
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
All 3-Wire Audio Serial Outputs Interrupt Mode
Configures the Interrupt and Signal Notification
condition of all 3-wire Audio Serial Outputs.
*/
#define PS3_AUDIO_AX_IC_AASOIMD_MASK (0x3 << 12) /* RWIVF */
#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1 (0x0 << 12) /* RWI-V */
#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2 (0x1 << 12) /* RW--V */
#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4 (0x2 << 12) /* RW--V */
/*
S/PDIF Output Channel Interrupt Modes
Configures the Interrupt and signal Notification
conditions of S/PDIF output channels.
*/
#define PS3_AUDIO_AX_IC_SPO1IMD_MASK (0x3 << 16) /* RWIVF */
#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1 (0x0 << 16) /* RWI-V */
#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2 (0x1 << 16) /* RW--V */
#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4 (0x2 << 16) /* RW--V */
#define PS3_AUDIO_AX_IC_SPO0IMD_MASK (0x3 << 20) /* RWIVF */
#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1 (0x0 << 20) /* RWI-V */
#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2 (0x1 << 20) /* RW--V */
#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4 (0x2 << 20) /* RW--V */
/*
Audio Port interrupt Enable Register
Configures whether to enable or disable each Interrupt Generation.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
3 Wire Audio Serial Output Channel Buffer Underflow
Interrupt Enables
Select enable/disable of Buffer Underflow Interrupts for
3-Wire Audio Serial Output Channels
DISABLED=Interrupt generation disabled.
*/
#define PS3_AUDIO_AX_IE_ASOBUIE(n) (1 << (3 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO3BUIE (1 << 0) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO2BUIE (1 << 1) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO1BUIE (1 << 2) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO0BUIE (1 << 3) /* RWIVF */
/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */
#define PS3_AUDIO_AX_IE_SPOBUIE(n) (1 << (7 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO1BUIE (1 << 6) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO0BUIE (1 << 7) /* RWIVF */
/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */
#define PS3_AUDIO_AX_IE_SPOBTCIE(n) (1 << (11 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO1BTCIE (1 << 10) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO0BTCIE (1 << 11) /* RWIVF */
/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */
#define PS3_AUDIO_AX_IE_ASOBEIE(n) (1 << (19 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO3BEIE (1 << 16) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO2BEIE (1 << 17) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO1BEIE (1 << 18) /* RWIVF */
#define PS3_AUDIO_AX_IE_ASO0BEIE (1 << 19) /* RWIVF */
/* S/PDIF Output Channel Buffer Empty Interrupt Enables */
#define PS3_AUDIO_AX_IE_SPOBEIE(n) (1 << (23 - (n))) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO1BEIE (1 << 22) /* RWIVF */
#define PS3_AUDIO_AX_IE_SPO0BEIE (1 << 23) /* RWIVF */
/*
Audio Port Interrupt Status Register
Indicates Interrupt status, which interrupt has occured, and can clear
each interrupt in this register.
Writing 1b to a field containing 1b clears field and de-asserts interrupt.
Writing 0b to a field has no effect.
Field vaules are the following:
0 - Interrupt hasn't occured.
1 - Interrupt has occured.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
Bit assignment are same as AX_IE
*/
/*
Audio Output Master Control Register
Configures Master Clock and other master Audio Output Settings
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0|SCKSE|0|SCKSE| MR0 | MR1 |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
MCLK Output Control
Controls mclko[1] output.
0 - Disable output (fixed at High)
1 - Output clock produced by clock selected
with scksel1 by mr1
2 - Reserved
3 - Reserved
*/
#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK (0x3 << 12) /* RWIVF */
#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED (0x0 << 12) /* RWI-V */
#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED (0x1 << 12) /* RW--V */
#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2 (0x2 << 12) /* RW--V */
#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3 (0x3 << 12) /* RW--V */
/*
MCLK Output Control
Controls mclko[0] output.
0 - Disable output (fixed at High)
1 - Output clock produced by clock selected
with SCKSEL0 by MR0
2 - Reserved
3 - Reserved
*/
#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK (0x3 << 14) /* RWIVF */
#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED (0x0 << 14) /* RWI-V */
#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED (0x1 << 14) /* RW--V */
#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2 (0x2 << 14) /* RW--V */
#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3 (0x3 << 14) /* RW--V */
/*
Master Clock Rate 1
Sets the divide ration of Master Clock1 (clock output from
mclko[1] for the input clock selected by scksel1.
*/
#define PS3_AUDIO_AO_MCTRL_MR1_MASK (0xf << 16)
#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT (0x0 << 16) /* RWI-V */
/*
Master Clock Rate 0
Sets the divide ratio of Master Clock0 (clock output from
mclko[0] for the input clock selected by scksel0).
*/
#define PS3_AUDIO_AO_MCTRL_MR0_MASK (0xf << 20) /* RWIVF */
#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT (0x0 << 20) /* RWI-V */
/*
System Clock Select 0/1
Selects the system clock to be used as Master Clock 0/1
Input the system clock that is appropriate for the sampling
rate.
*/
#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK (0x7 << 24) /* RWIVF */
#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT (0x2 << 24) /* RWI-V */
#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK (0x7 << 28) /* RWIVF */
#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT (0x2 << 28) /* RWI-V */
/*
3-Wire Audio Output Master Control Register
Configures clock, 3-Wire Audio Serial Output Enable, and
other 3-Wire Audio Serial Output Master Settings
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
LRCKO Polarity
0 - Reserved
1 - default
*/
#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK (1 << 8) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT (1 << 8) /* RW--V */
/* LRCK Output Disable */
#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD (1 << 10) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED (0 << 10) /* RW--V */
#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED (1 << 10) /* RWI-V */
/* Bit Clock Output Disable */
#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD (1 << 11) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED (0 << 11) /* RW--V */
#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED (1 << 11) /* RWI-V */
/*
3-Wire Audio Serial Output Channel 0-3 Operational
Status. Each bit becomes 1 after each 3-Wire Audio
Serial Output Channel N is in action by setting 1 to
asoen.
Each bit becomes 0 after each 3-Wire Audio Serial Output
Channel N is out of action by setting 0 to asoen.
*/
#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n) (1 << (15 - (n))) /* R-IVF */
#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n) (0 << (15 - (n))) /* R-I-V */
#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n) (1 << (15 - (n))) /* R---V */
#define PS3_AUDIO_AO_3WMCTRL_ASORUN0 \
PS3_AUDIO_AO_3WMCTRL_ASORUN(0)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED \
PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING \
PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN1 \
PS3_AUDIO_AO_3WMCTRL_ASORUN(1)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED \
PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING \
PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN2 \
PS3_AUDIO_AO_3WMCTRL_ASORUN(2)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED \
PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING \
PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN3 \
PS3_AUDIO_AO_3WMCTRL_ASORUN(3)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED \
PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3)
#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING \
PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3)
/*
Sampling Rate
Specifies the divide ratio of the bit clock (clock output
from bclko) used by the 3-wire Audio Output Clock, whcih
is applied to the master clock selected by mcksel.
Data output is synchronized with this clock.
*/
#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK (0xf << 20) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2 (0x1 << 20) /* RWI-V */
#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4 (0x2 << 20) /* RW--V */
#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8 (0x4 << 20) /* RW--V */
#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12 (0x6 << 20) /* RW--V */
/*
Master Clock Select
0 - Master Clock 0
1 - Master Clock 1
*/
#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL (1 << 24) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0 (0 << 24) /* RWI-V */
#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1 (1 << 24) /* RW--V */
/*
Enables and disables 4ch 3-Wire Audio Serial Output
operation. Each Bit from 0 to 3 corresponds to an
output channel, which means that each output channel
can be enabled or disabled individually. When
multiple channels are enabled at the same time, output
operations are performed in synchronization.
Bit 0 - Output Channel 0 (SDOUT[0])
Bit 1 - Output Channel 1 (SDOUT[1])
Bit 2 - Output Channel 2 (SDOUT[2])
Bit 3 - Output Channel 3 (SDOUT[3])
*/
#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n) (1 << (31 - (n))) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n) (0 << (31 - (n))) /* RWI-V */
#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n) (1 << (31 - (n))) /* RW--V */
#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \
PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */
#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */
#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */
#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \
PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */
#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */
#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */
#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \
PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */
#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */
#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */
#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \
PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */
#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */
#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \
PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */
/*
3-Wire Audio Serial output Channel 0-3 Control Register
Configures settings for 3-Wire Serial Audio Output Channel 0-3
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
Data Bit Mode
Specifies the number of data bits
0 - 16 bits
1 - reserved
2 - 20 bits
3 - 24 bits
*/
#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK (0x3 << 8) /* RWIVF */
#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT (0x0 << 8) /* RWI-V */
#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD (0x1 << 8) /* RWI-V */
#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT (0x2 << 8) /* RW--V */
#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT (0x3 << 8) /* RW--V */
/*
Data Format Mode
Specifies the data format where (LSB side or MSB) the data(in 20 bit
or 24 bit resolution mode) is put in a 32 bit field.
0 - Data put on LSB side
1 - Data put on MSB side
*/
#define PS3_AUDIO_AO_3WCTRL_ASODF (1 << 11) /* RWIVF */
#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB (0 << 11) /* RWI-V */
#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB (1 << 11) /* RW--V */
/*
Buffer Reset
Performs buffer reset. Writing 1 to this bit initializes the
corresponding 3-Wire Audio Output buffers(both L and R).
*/
#define PS3_AUDIO_AO_3WCTRL_ASOBRST (1 << 16) /* CWIVF */
#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE (0 << 16) /* -WI-V */
#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET (1 << 16) /* -W--T */
/*
S/PDIF Audio Output Channel 0/1 Control Register
Configures settings for S/PDIF Audio Output Channel 0/1.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
Buffer reset. Writing 1 to this bit initializes the
corresponding S/PDIF output buffer pointer.
*/
#define PS3_AUDIO_AO_SPDCTRL_SPOBRST (1 << 0) /* CWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE (0 << 0) /* -WI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET (1 << 0) /* -W--T */
/*
Data Bit Mode
Specifies number of data bits
0 - 16 bits
1 - Reserved
2 - 20 bits
3 - 24 bits
*/
#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK (0x3 << 8) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT (0x0 << 8) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD (0x1 << 8) /* RW--V */
#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT (0x2 << 8) /* RW--V */
#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT (0x3 << 8) /* RW--V */
/*
Data format Mode
Specifies the data format, where (LSB side or MSB)
the data(in 20 or 24 bit resolution) is put in the
32 bit field.
0 - LSB Side
1 - MSB Side
*/
#define PS3_AUDIO_AO_SPDCTRL_SPODF (1 << 11) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB (0 << 11) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB (1 << 11) /* RW--V */
/*
Source Select
Specifies the source of the S/PDIF output. When 0, output
operation is controlled by 3wen[0] of AO_3WMCTRL register.
The SR must have the same setting as the a0_3wmctrl reg.
0 - 3-Wire Audio OUT Ch0 Buffer
1 - S/PDIF buffer
*/
#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK (0x3 << 16) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN (0x0 << 16) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF (0x1 << 16) /* RW--V */
/*
Sampling Rate
Specifies the divide ratio of the bit clock (clock output
from bclko) used by the S/PDIF Output Clock, which
is applied to the master clock selected by mcksel.
*/
#define PS3_AUDIO_AO_SPDCTRL_SPOSR (0xf << 20) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2 (0x1 << 20) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4 (0x2 << 20) /* RW--V */
#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8 (0x4 << 20) /* RW--V */
#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12 (0x6 << 20) /* RW--V */
/*
Master Clock Select
0 - Master Clock 0
1 - Master Clock 1
*/
#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL (1 << 24) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0 (0 << 24) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1 (1 << 24) /* RW--V */
/*
S/PDIF Output Channel Operational Status
This bit becomes 1 after S/PDIF Output Channel is in
action by setting 1 to spoen. This bit becomes 0
after S/PDIF Output Channel is out of action by setting
0 to spoen.
*/
#define PS3_AUDIO_AO_SPDCTRL_SPORUN (1 << 27) /* R-IVF */
#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED (0 << 27) /* R-I-V */
#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING (1 << 27) /* R---V */
/*
S/PDIF Audio Output Channel Output Enable
Enables and disables output operation. This bit is used
only when sposs = 1
*/
#define PS3_AUDIO_AO_SPDCTRL_SPOEN (1 << 31) /* RWIVF */
#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED (0 << 31) /* RWI-V */
#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED (1 << 31) /* RW--V */
/*
S/PDIF Audio Output Channel Channel Status
Setting Registers.
Configures channel status bit settings for each block
(192 bits).
Output is performed from the MSB(AO_SPDCS0 register bit 31).
The same value is added for subframes within the same frame.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
| SPOCS | AO_SPDCS
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
S/PDIF Audio Output Channel User Bit Setting
Configures user bit settings for each block (384 bits).
Output is performed from the MSB(ao_spdub0 register bit 31).
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
| SPOUB | AO_SPDUB
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*****************************************************************************
*
* DMAC register
*
*****************************************************************************/
/*
The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor
its status
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0|STATU|0 0 0| EVENT |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT
occurs.
It will return to the DONE state when the request is completed.
The registers for a DMA channel should only be written if REQUEST is IDLE.
*/
#define PS3_AUDIO_KICK_REQUEST (1 << 0) /* RWIVF */
#define PS3_AUDIO_KICK_REQUEST_IDLE (0 << 0) /* RWI-V */
#define PS3_AUDIO_KICK_REQUEST_ACTIVE (1 << 0) /* -W--T */
/*
*The EVENT field is used to set the event in which
*the DMA request becomes active.
*/
#define PS3_AUDIO_KICK_EVENT_MASK (0x1f << 16) /* RWIVF */
#define PS3_AUDIO_KICK_EVENT_ALWAYS (0x00 << 16) /* RWI-V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW (0x02 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY (0x03 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW (0x04 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY (0x05 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW (0x06 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY (0x07 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW (0x08 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \
(0x09 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW (0x0A << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY (0x0B << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \
(0x0C << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW (0x0D << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY (0x0E << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \
((0x13 + (n)) << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0 (0x13 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1 (0x14 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2 (0x15 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3 (0x16 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4 (0x17 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5 (0x18 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6 (0x19 << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7 (0x1A << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8 (0x1B << 16) /* RW--V */
#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9 (0x1C << 16) /* RW--V */
/*
The STATUS field can be used to monitor the progress of a DMA request.
DONE indicates the previous request has completed.
EVENT indicates that the DMA engine is waiting for the EVENT to occur.
PENDING indicates that the DMA engine has not started processing this
request, but the EVENT has occured.
DMA indicates that the data transfer is in progress.
NOTIFY indicates that the notifier signalling end of transfer is being written.
CLEAR indicated that the previous transfer was cleared.
ERROR indicates the previous transfer requested an unsupported
source/destination combination.
*/
#define PS3_AUDIO_KICK_STATUS_MASK (0x7 << 24) /* R-IVF */
#define PS3_AUDIO_KICK_STATUS_DONE (0x0 << 24) /* R-I-V */
#define PS3_AUDIO_KICK_STATUS_EVENT (0x1 << 24) /* R---V */
#define PS3_AUDIO_KICK_STATUS_PENDING (0x2 << 24) /* R---V */
#define PS3_AUDIO_KICK_STATUS_DMA (0x3 << 24) /* R---V */
#define PS3_AUDIO_KICK_STATUS_NOTIFY (0x4 << 24) /* R---V */
#define PS3_AUDIO_KICK_STATUS_CLEAR (0x5 << 24) /* R---V */
#define PS3_AUDIO_KICK_STATUS_ERROR (0x6 << 24) /* R---V */
/*
The PS3_AUDIO_SOURCE register specifies the source address for transfers.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
| START |0 0 0 0 0|TAR| SOURCE
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
to a 128 byte boundary. The low seven bits are assumed to be 0.
*/
#define PS3_AUDIO_SOURCE_START_MASK (0x01FFFFFF << 7) /* RWIUF */
/*
The TARGET field specifies the memory space containing the source address.
*/
#define PS3_AUDIO_SOURCE_TARGET_MASK (3 << 0) /* RWIVF */
#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY (2 << 0) /* RW--V */
/*
The PS3_AUDIO_DEST register specifies the destination address for transfers.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
| START |0 0 0 0 0|TAR| DEST
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
/*
The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
to a 128 byte boundary. The low seven bits are assumed to be 0.
*/
#define PS3_AUDIO_DEST_START_MASK (0x01FFFFFF << 7) /* RWIUF */
/*
The TARGET field specifies the memory space containing the destination address
AUDIOFIFO = Audio WriteData FIFO,
*/
#define PS3_AUDIO_DEST_TARGET_MASK (3 << 0) /* RWIVF */
#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO (1 << 0) /* RW--V */
/*
PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer.
So a value of 0 means 128-bytes will get transfered.
31 24 23 16 15 8 7 0
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| BLOCKS | DMASIZE
+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
*/
#define PS3_AUDIO_DMASIZE_BLOCKS_MASK (0x7f << 0) /* RWIUF */
/*
* source/destination address for internal fifos
*/
#define PS3_AUDIO_AO_3W_LDATA(n) (0x1000 + (0x100 * (n)))
#define PS3_AUDIO_AO_3W_RDATA(n) (0x1080 + (0x100 * (n)))
#define PS3_AUDIO_AO_SPD_DATA(n) (0x2000 + (0x400 * (n)))
/*
* field attiribute
*
* Read
* ' ' = Other Information
* '-' = Field is part of a write-only register
* 'C' = Value read is always the same, constant value line follows (C)
* 'R' = Value is read
*
* Write
* ' ' = Other Information
* '-' = Must not be written (D), value ignored when written (R,A,F)
* 'W' = Can be written
*
* Internal State
* ' ' = Other Information
* '-' = No internal state
* 'X' = Internal state, initial value is unknown
* 'I' = Internal state, initial value is known and follows (I)
*
* Declaration/Size
* ' ' = Other Information
* '-' = Does Not Apply
* 'V' = Type is void
* 'U' = Type is unsigned integer
* 'S' = Type is signed integer
* 'F' = Type is IEEE floating point
* '1' = Byte size (008)
* '2' = Short size (016)
* '3' = Three byte size (024)
* '4' = Word size (032)
* '8' = Double size (064)
*
* Define Indicator
* ' ' = Other Information
* 'D' = Device
* 'M' = Memory
* 'R' = Register
* 'A' = Array of Registers
* 'F' = Field
* 'V' = Value
* 'T' = Task
*/

14
sound/sh/Kconfig Normal file
View File

@ -0,0 +1,14 @@
# ALSA SH drivers
menu "SUPERH devices"
depends on SND!=n && SUPERH
config SND_AICA
tristate "Dreamcast Yamaha AICA sound"
depends on SH_DREAMCAST && SND
select SND_PCM
help
ALSA Sound driver for the SEGA Dreamcast console.
endmenu

8
sound/sh/Makefile Normal file
View File

@ -0,0 +1,8 @@
#
# Makefile for ALSA
#
snd-aica-objs := aica.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AICA) += snd-aica.o

665
sound/sh/aica.c Normal file
View File

@ -0,0 +1,665 @@
/*
* This code is licenced under
* the General Public Licence
* version 2
*
* Copyright Adrian McMenamin 2005, 2006, 2007
* <adrian@mcmen.demon.co.uk>
* Requires firmware (BSD licenced) available from:
* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
* or the maintainer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as published by
* the Free Software Foundation.
*
* 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
*
*/
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/info.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/dreamcast/sysasic.h>
#include "aica.h"
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
/* module parameters */
#define CARD_NAME "AICA"
static int index = -1;
static char *id;
static int enable = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
module_param(enable, bool, 0644);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
/* Use workqueue */
static struct workqueue_struct *aica_queue;
/* Simple platform device */
static struct platform_device *pd;
static struct resource aica_memory_space[2] = {
{
.name = "AICA ARM CONTROL",
.start = ARM_RESET_REGISTER,
.flags = IORESOURCE_MEM,
.end = ARM_RESET_REGISTER + 3,
},
{
.name = "AICA Sound RAM",
.start = SPU_MEMORY_BASE,
.flags = IORESOURCE_MEM,
.end = SPU_MEMORY_BASE + 0x200000 - 1,
},
};
/* SPU specific functions */
/* spu_write_wait - wait for G2-SH FIFO to clear */
static void spu_write_wait(void)
{
int time_count;
time_count = 0;
while (1) {
if (!(readl(G2_FIFO) & 0x11))
break;
/* To ensure hardware failure doesn't wedge kernel */
time_count++;
if (time_count > 0x10000) {
snd_printk
("WARNING: G2 FIFO appears to be blocked.\n");
break;
}
}
}
/* spu_memset - write to memory in SPU address space */
static void spu_memset(u32 toi, u32 what, int length)
{
int i;
snd_assert(length % 4 == 0, return);
for (i = 0; i < length; i++) {
if (!(i % 8))
spu_write_wait();
writel(what, toi + SPU_MEMORY_BASE);
toi++;
}
}
/* spu_memload - write to SPU address space */
static void spu_memload(u32 toi, void *from, int length)
{
u32 *froml = from;
u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
int i;
u32 val;
length = DIV_ROUND_UP(length, 4);
spu_write_wait();
for (i = 0; i < length; i++) {
if (!(i % 8))
spu_write_wait();
val = *froml;
writel(val, to);
froml++;
to++;
}
}
/* spu_disable - set spu registers to stop sound output */
static void spu_disable(void)
{
int i;
u32 regval;
spu_write_wait();
regval = readl(ARM_RESET_REGISTER);
regval |= 1;
spu_write_wait();
writel(regval, ARM_RESET_REGISTER);
for (i = 0; i < 64; i++) {
spu_write_wait();
regval = readl(SPU_REGISTER_BASE + (i * 0x80));
regval = (regval & ~0x4000) | 0x8000;
spu_write_wait();
writel(regval, SPU_REGISTER_BASE + (i * 0x80));
}
}
/* spu_enable - set spu registers to enable sound output */
static void spu_enable(void)
{
u32 regval = readl(ARM_RESET_REGISTER);
regval &= ~1;
spu_write_wait();
writel(regval, ARM_RESET_REGISTER);
}
/*
* Halt the sound processor, clear the memory,
* load some default ARM7 code, and then restart ARM7
*/
static void spu_reset(void)
{
spu_disable();
spu_memset(0, 0, 0x200000 / 4);
/* Put ARM7 in endless loop */
ctrl_outl(0xea000002, SPU_MEMORY_BASE);
spu_enable();
}
/* aica_chn_start - write to spu to start playback */
static void aica_chn_start(void)
{
spu_write_wait();
writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
}
/* aica_chn_halt - write to spu to halt playback */
static void aica_chn_halt(void)
{
spu_write_wait();
writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
}
/* ALSA code below */
static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
.formats =
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_IMA_ADPCM),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = AICA_BUFFER_SIZE,
.period_bytes_min = AICA_PERIOD_SIZE,
.period_bytes_max = AICA_PERIOD_SIZE,
.periods_min = AICA_PERIOD_NUMBER,
.periods_max = AICA_PERIOD_NUMBER,
};
static int aica_dma_transfer(int channels, int buffer_size,
struct snd_pcm_substream *substream)
{
int q, err, period_offset;
struct snd_card_aica *dreamcastcard;
struct snd_pcm_runtime *runtime;
err = 0;
dreamcastcard = substream->pcm->private_data;
period_offset = dreamcastcard->clicks;
period_offset %= (AICA_PERIOD_NUMBER / channels);
runtime = substream->runtime;
for (q = 0; q < channels; q++) {
err = dma_xfer(AICA_DMA_CHANNEL,
(unsigned long) (runtime->dma_area +
(AICA_BUFFER_SIZE * q) /
channels +
AICA_PERIOD_SIZE *
period_offset),
AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
AICA_PERIOD_SIZE * period_offset,
buffer_size / channels, AICA_DMA_MODE);
if (unlikely(err < 0))
break;
dma_wait_for_completion(AICA_DMA_CHANNEL);
}
return err;
}
static void startup_aica(struct snd_card_aica *dreamcastcard)
{
spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
dreamcastcard->channel, sizeof(struct aica_channel));
aica_chn_start();
}
static void run_spu_dma(struct work_struct *work)
{
int buffer_size;
struct snd_pcm_runtime *runtime;
struct snd_card_aica *dreamcastcard;
dreamcastcard =
container_of(work, struct snd_card_aica, spu_dma_work);
runtime = dreamcastcard->substream->runtime;
if (unlikely(dreamcastcard->dma_check == 0)) {
buffer_size =
frames_to_bytes(runtime, runtime->buffer_size);
if (runtime->channels > 1)
dreamcastcard->channel->flags |= 0x01;
aica_dma_transfer(runtime->channels, buffer_size,
dreamcastcard->substream);
startup_aica(dreamcastcard);
dreamcastcard->clicks =
buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
return;
} else {
aica_dma_transfer(runtime->channels,
AICA_PERIOD_SIZE * runtime->channels,
dreamcastcard->substream);
snd_pcm_period_elapsed(dreamcastcard->substream);
dreamcastcard->clicks++;
if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
mod_timer(&dreamcastcard->timer, jiffies + 1);
}
}
static void aica_period_elapsed(unsigned long timer_var)
{
/*timer function - so cannot sleep */
int play_period;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *substream;
struct snd_card_aica *dreamcastcard;
substream = (struct snd_pcm_substream *) timer_var;
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/* Have we played out an additional period? */
play_period =
frames_to_bytes(runtime,
readl
(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
AICA_PERIOD_SIZE;
if (play_period == dreamcastcard->current_period) {
/* reschedule the timer */
mod_timer(&(dreamcastcard->timer), jiffies + 1);
return;
}
if (runtime->channels > 1)
dreamcastcard->current_period = play_period;
if (unlikely(dreamcastcard->dma_check == 0))
dreamcastcard->dma_check = 1;
queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
}
static void spu_begin_dma(struct snd_pcm_substream *substream)
{
struct snd_card_aica *dreamcastcard;
struct snd_pcm_runtime *runtime;
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
/* Timer may already be running */
if (unlikely(dreamcastcard->timer.data)) {
mod_timer(&dreamcastcard->timer, jiffies + 4);
return;
}
init_timer(&(dreamcastcard->timer));
dreamcastcard->timer.data = (unsigned long) substream;
dreamcastcard->timer.function = aica_period_elapsed;
dreamcastcard->timer.expires = jiffies + 4;
add_timer(&(dreamcastcard->timer));
}
static int snd_aicapcm_pcm_open(struct snd_pcm_substream
*substream)
{
struct snd_pcm_runtime *runtime;
struct aica_channel *channel;
struct snd_card_aica *dreamcastcard;
if (!enable)
return -ENOENT;
dreamcastcard = substream->pcm->private_data;
channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
if (!channel)
return -ENOMEM;
/* set defaults for channel */
channel->sfmt = SM_8BIT;
channel->cmd = AICA_CMD_START;
channel->vol = dreamcastcard->master_volume;
channel->pan = 0x80;
channel->pos = 0;
channel->flags = 0; /* default to mono */
dreamcastcard->channel = channel;
runtime = substream->runtime;
runtime->hw = snd_pcm_aica_playback_hw;
spu_enable();
dreamcastcard->clicks = 0;
dreamcastcard->current_period = 0;
dreamcastcard->dma_check = 0;
return 0;
}
static int snd_aicapcm_pcm_close(struct snd_pcm_substream
*substream)
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
flush_workqueue(aica_queue);
if (dreamcastcard->timer.data)
del_timer(&dreamcastcard->timer);
kfree(dreamcastcard->channel);
spu_disable();
return 0;
}
static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
*substream)
{
/* Free the DMA buffer */
return snd_pcm_lib_free_pages(substream);
}
static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
*substream, struct snd_pcm_hw_params
*hw_params)
{
/* Allocate a DMA buffer using ALSA built-ins */
return
snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
*substream)
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
dreamcastcard->channel->sfmt = SM_16BIT;
dreamcastcard->channel->freq = substream->runtime->rate;
dreamcastcard->substream = substream;
return 0;
}
static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
*substream, int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
spu_begin_dma(substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
aica_chn_halt();
break;
default:
return -EINVAL;
}
return 0;
}
static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
*substream)
{
return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
}
static struct snd_pcm_ops snd_aicapcm_playback_ops = {
.open = snd_aicapcm_pcm_open,
.close = snd_aicapcm_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_aicapcm_pcm_hw_params,
.hw_free = snd_aicapcm_pcm_hw_free,
.prepare = snd_aicapcm_pcm_prepare,
.trigger = snd_aicapcm_pcm_trigger,
.pointer = snd_aicapcm_pcm_pointer,
};
/* TO DO: set up to handle more than one pcm instance */
static int __init snd_aicapcmchip(struct snd_card_aica
*dreamcastcard, int pcm_index)
{
struct snd_pcm *pcm;
int err;
/* AICA has no capture ability */
err =
snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
&pcm);
if (unlikely(err < 0))
return err;
pcm->private_data = dreamcastcard;
strcpy(pcm->name, "AICA PCM");
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_aicapcm_playback_ops);
/* Allocate the DMA buffers */
err =
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data
(GFP_KERNEL),
AICA_BUFFER_SIZE,
AICA_BUFFER_SIZE);
return err;
}
/* Mixer controls */
static int aica_pcmswitch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */
return 0;
}
static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
if (ucontrol->value.integer.value[0] == 1)
return 0; /* TO DO: Fix me */
else
aica_chn_halt();
return 0;
}
static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0xFF;
return 0;
}
static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_card_aica *dreamcastcard;
dreamcastcard = kcontrol->private_data;
if (unlikely(!dreamcastcard->channel))
return -ETXTBSY; /* we've not yet been set up */
ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
return 0;
}
static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_card_aica *dreamcastcard;
dreamcastcard = kcontrol->private_data;
if (unlikely(!dreamcastcard->channel))
return -ETXTBSY;
if (unlikely(dreamcastcard->channel->vol ==
ucontrol->value.integer.value[0]))
return 0;
dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
dreamcastcard->master_volume = ucontrol->value.integer.value[0];
spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
dreamcastcard->channel, sizeof(struct aica_channel));
return 1;
}
static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
.info = aica_pcmswitch_info,
.get = aica_pcmswitch_get,
.put = aica_pcmswitch_put
};
static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
.info = aica_pcmvolume_info,
.get = aica_pcmvolume_get,
.put = aica_pcmvolume_put
};
static int load_aica_firmware(void)
{
int err;
const struct firmware *fw_entry;
spu_reset();
err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
if (unlikely(err))
return err;
/* write firware into memory */
spu_disable();
spu_memload(0, fw_entry->data, fw_entry->size);
spu_enable();
release_firmware(fw_entry);
return err;
}
static int __devinit add_aicamixer_controls(struct snd_card_aica
*dreamcastcard)
{
int err;
err = snd_ctl_add
(dreamcastcard->card,
snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
if (unlikely(err < 0))
return err;
err = snd_ctl_add
(dreamcastcard->card,
snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
if (unlikely(err < 0))
return err;
return 0;
}
static int snd_aica_remove(struct platform_device *devptr)
{
struct snd_card_aica *dreamcastcard;
dreamcastcard = platform_get_drvdata(devptr);
if (unlikely(!dreamcastcard))
return -ENODEV;
snd_card_free(dreamcastcard->card);
kfree(dreamcastcard);
platform_set_drvdata(devptr, NULL);
return 0;
}
static int __init snd_aica_probe(struct platform_device *devptr)
{
int err;
struct snd_card_aica *dreamcastcard;
dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
if (unlikely(!dreamcastcard))
return -ENOMEM;
dreamcastcard->card =
snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
if (unlikely(!dreamcastcard->card)) {
kfree(dreamcastcard);
return -ENODEV;
}
strcpy(dreamcastcard->card->driver, "snd_aica");
strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
strcpy(dreamcastcard->card->longname,
"Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
/* Prepare to use the queue */
INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
/* Load the PCM 'chip' */
err = snd_aicapcmchip(dreamcastcard, 0);
if (unlikely(err < 0))
goto freedreamcast;
snd_card_set_dev(dreamcastcard->card, &devptr->dev);
dreamcastcard->timer.data = 0;
dreamcastcard->channel = NULL;
/* Add basic controls */
err = add_aicamixer_controls(dreamcastcard);
if (unlikely(err < 0))
goto freedreamcast;
/* Register the card with ALSA subsystem */
err = snd_card_register(dreamcastcard->card);
if (unlikely(err < 0))
goto freedreamcast;
platform_set_drvdata(devptr, dreamcastcard);
aica_queue = create_workqueue(CARD_NAME);
if (unlikely(!aica_queue))
goto freedreamcast;
snd_printk
("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
return 0;
freedreamcast:
snd_card_free(dreamcastcard->card);
kfree(dreamcastcard);
return err;
}
static struct platform_driver snd_aica_driver = {
.probe = snd_aica_probe,
.remove = snd_aica_remove,
.driver = {
.name = SND_AICA_DRIVER},
};
static int __init aica_init(void)
{
int err;
err = platform_driver_register(&snd_aica_driver);
if (unlikely(err < 0))
return err;
pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
aica_memory_space, 2);
if (unlikely(IS_ERR(pd))) {
platform_driver_unregister(&snd_aica_driver);
return PTR_ERR(pd);
}
/* Load the firmware */
return load_aica_firmware();
}
static void __exit aica_exit(void)
{
/* Destroy the aica kernel thread *
* being extra cautious to check if it exists*/
if (likely(aica_queue))
destroy_workqueue(aica_queue);
platform_device_unregister(pd);
platform_driver_unregister(&snd_aica_driver);
/* Kill any sound still playing and reset ARM7 to safe state */
spu_reset();
}
module_init(aica_init);
module_exit(aica_exit);

81
sound/sh/aica.h Normal file
View File

@ -0,0 +1,81 @@
/* aica.h
* Header file for ALSA driver for
* Sega Dreamcast Yamaha AICA sound
* Copyright Adrian McMenamin
* <adrian@mcmen.demon.co.uk>
* 2006
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as published by
* the Free Software Foundation.
*
* 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
*
*/
/* SPU memory and register constants etc */
#define G2_FIFO 0xa05f688c
#define SPU_MEMORY_BASE 0xA0800000
#define ARM_RESET_REGISTER 0xA0702C00
#define SPU_REGISTER_BASE 0xA0700000
/* AICA channels stuff */
#define AICA_CONTROL_POINT 0xA0810000
#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008
#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004
/* Command values */
#define AICA_CMD_KICK 0x80000000
#define AICA_CMD_NONE 0
#define AICA_CMD_START 1
#define AICA_CMD_STOP 2
#define AICA_CMD_VOL 3
/* Sound modes */
#define SM_8BIT 1
#define SM_16BIT 0
#define SM_ADPCM 2
/* Buffer and period size */
#define AICA_BUFFER_SIZE 0x8000
#define AICA_PERIOD_SIZE 0x800
#define AICA_PERIOD_NUMBER 16
#define AICA_CHANNEL0_OFFSET 0x11000
#define AICA_CHANNEL1_OFFSET 0x21000
#define CHANNEL_OFFSET 0x10000
#define AICA_DMA_CHANNEL 0
#define AICA_DMA_MODE 5
#define SND_AICA_DRIVER "AICA"
struct aica_channel {
uint32_t cmd; /* Command ID */
uint32_t pos; /* Sample position */
uint32_t length; /* Sample length */
uint32_t freq; /* Frequency */
uint32_t vol; /* Volume 0-255 */
uint32_t pan; /* Pan 0-255 */
uint32_t sfmt; /* Sound format */
uint32_t flags; /* Bit flags */
};
struct snd_card_aica {
struct work_struct spu_dma_work;
struct snd_card *card;
struct aica_channel *channel;
struct snd_pcm_substream *substream;
int clicks;
int current_period;
struct timer_list timer;
int master_volume;
int dma_check;
};

View File

@ -27,6 +27,7 @@ config SND_SOC
source "sound/soc/at91/Kconfig" source "sound/soc/at91/Kconfig"
source "sound/soc/pxa/Kconfig" source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig" source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
# Supported codecs # Supported codecs
source "sound/soc/codecs/Kconfig" source "sound/soc/codecs/Kconfig"

View File

@ -1,4 +1,4 @@
snd-soc-core-objs := soc-core.o soc-dapm.o snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/

View File

@ -1,6 +1,7 @@
config SND_S3C24XX_SOC config SND_S3C24XX_SOC
tristate "SoC Audio for the Samsung S3C24XX chips" tristate "SoC Audio for the Samsung S3C24XX chips"
depends on ARCH_S3C2410 && SND_SOC depends on ARCH_S3C2410 && SND_SOC
select SND_PCM
help help
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the S3C24XX AC97, I2S or SSP interface. You will also need the S3C24XX AC97, I2S or SSP interface. You will also need
@ -8,3 +9,29 @@ config SND_S3C24XX_SOC
config SND_S3C24XX_SOC_I2S config SND_S3C24XX_SOC_I2S
tristate tristate
config SND_S3C2443_SOC_AC97
tristate
select AC97_BUS
select SND_AC97_CODEC
select SND_SOC_AC97_BUS
config SND_S3C24XX_SOC_NEO1973_WM8753
tristate "SoC I2S Audio support for NEO1973 - WM8753"
depends on SND_S3C24XX_SOC && MACH_GTA01
select SND_S3C24XX_SOC_I2S
select SND_SOC_WM8753
help
Say Y if you want to add support for SoC audio on smdk2440
with the WM8753.
config SND_S3C24XX_SOC_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
depends on SND_S3C24XX_SOC && MACH_SMDK2443
select SND_S3C2443_SOC_AC97
select SND_SOC_AC97_CODEC
help
Say Y if you want to add support for SoC audio on smdk2443
with the WM9710.

View File

@ -1,6 +1,15 @@
# S3c24XX Platform Support # S3c24XX Platform Support
snd-soc-s3c24xx-objs := s3c24xx-pcm.o snd-soc-s3c24xx-objs := s3c24xx-pcm.o
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
# S3C24XX Machine Support
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o

View File

@ -0,0 +1,32 @@
/*
* lm4857.h -- ALSA Soc Audio Layer
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
*
* Revision history
* 18th Jun 2007 Initial version.
*/
#ifndef LM4857_H_
#define LM4857_H_
/* The register offsets in the cache array */
#define LM4857_MVOL 0
#define LM4857_LVOL 1
#define LM4857_RVOL 2
#define LM4857_CTRL 3
/* the shifts required to set these bits */
#define LM4857_3D 5
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
#endif /*LM4857_H_*/

View File

@ -0,0 +1,670 @@
/*
* neo1973_wm8753.c -- SoC audio for Neo1973
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
*
* Revision history
* 20th Jan 2007 Initial version.
* 05th Feb 2007 Rename all to Neo1973
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <asm/hardware/scoop.h>
#include <asm/arch/regs-iis.h>
#include <asm/arch/regs-clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/arch/audio.h>
#include <asm/io.h>
#include <asm/arch/spi-gpio.h>
#include "../codecs/wm8753.h"
#include "lm4857.h"
#include "s3c24xx-pcm.h"
#include "s3c24xx-i2s.h"
/* define the scenarios */
#define NEO_AUDIO_OFF 0
#define NEO_GSM_CALL_AUDIO_HANDSET 1
#define NEO_GSM_CALL_AUDIO_HEADSET 2
#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
#define NEO_STEREO_TO_SPEAKERS 4
#define NEO_STEREO_TO_HEADPHONES 5
#define NEO_CAPTURE_HANDSET 6
#define NEO_CAPTURE_HEADSET 7
#define NEO_CAPTURE_BLUETOOTH 8
static struct snd_soc_machine neo1973;
static struct i2c_client *i2c;
static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
unsigned int pll_out = 0, bclk = 0;
int ret = 0;
unsigned long iis_clkrate;
iis_clkrate = s3c24xx_i2s_get_clockrate();
switch (params_rate(params)) {
case 8000:
case 16000:
pll_out = 12288000;
break;
case 48000:
bclk = WM8753_BCLK_DIV_4;
pll_out = 12288000;
break;
case 96000:
bclk = WM8753_BCLK_DIV_2;
pll_out = 12288000;
break;
case 11025:
bclk = WM8753_BCLK_DIV_16;
pll_out = 11289600;
break;
case 22050:
bclk = WM8753_BCLK_DIV_8;
pll_out = 11289600;
break;
case 44100:
bclk = WM8753_BCLK_DIV_4;
pll_out = 11289600;
break;
case 88200:
bclk = WM8753_BCLK_DIV_2;
pll_out = 11289600;
break;
}
/* set codec DAI configuration */
ret = codec_dai->dai_ops.set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set MCLK division for sample rate */
ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
S3C2410_IISMOD_32FS );
if (ret < 0)
return ret;
/* set codec BCLK division for sample rate */
ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
if (ret < 0)
return ret;
/* set prescaler division for sample rate */
ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
S3C24XX_PRESCALE(4,4));
if (ret < 0)
return ret;
/* codec PLL input is PCLK/4 */
ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
iis_clkrate / 4, pll_out);
if (ret < 0)
return ret;
return 0;
}
static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
}
/*
* Neo1973 WM8753 HiFi DAI opserations.
*/
static struct snd_soc_ops neo1973_hifi_ops = {
.hw_params = neo1973_hifi_hw_params,
.hw_free = neo1973_hifi_hw_free,
};
static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
unsigned int pcmdiv = 0;
int ret = 0;
unsigned long iis_clkrate;
iis_clkrate = s3c24xx_i2s_get_clockrate();
if (params_rate(params) != 8000)
return -EINVAL;
if (params_channels(params) != 1)
return -EINVAL;
pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
/* todo: gg check mode (DSP_B) against CSR datasheet */
/* set codec DAI configuration */
ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (ret < 0)
return ret;
/* set the codec system clock for DAC and ADC */
ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* set codec PCM division for sample rate */
ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
if (ret < 0)
return ret;
/* configue and enable PLL for 12.288MHz output */
ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
iis_clkrate / 4, 12288000);
if (ret < 0)
return ret;
return 0;
}
static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
/* disable the PLL */
return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
}
static struct snd_soc_ops neo1973_voice_ops = {
.hw_params = neo1973_voice_hw_params,
.hw_free = neo1973_voice_hw_free,
};
static int neo1973_scenario = 0;
static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.integer.value[0] = neo1973_scenario;
return 0;
}
static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
{
switch(neo1973_scenario) {
case NEO_AUDIO_OFF:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_GSM_CALL_AUDIO_HANDSET:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
break;
case NEO_GSM_CALL_AUDIO_HEADSET:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_GSM_CALL_AUDIO_BLUETOOTH:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_STEREO_TO_SPEAKERS:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_STEREO_TO_HEADPHONES:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_CAPTURE_HANDSET:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
break;
case NEO_CAPTURE_HEADSET:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
case NEO_CAPTURE_BLUETOOTH:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
break;
default:
snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
}
snd_soc_dapm_sync_endpoints(codec);
return 0;
}
static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
if (neo1973_scenario == ucontrol->value.integer.value[0])
return 0;
neo1973_scenario = ucontrol->value.integer.value[0];
set_scenario_endpoints(codec, neo1973_scenario);
return 1;
}
static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
static void lm4857_write_regs(void)
{
if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
printk(KERN_ERR "lm4857: i2c write failed\n");
}
static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int reg=kcontrol->private_value & 0xFF;
int shift = (kcontrol->private_value >> 8) & 0x0F;
int mask = (kcontrol->private_value >> 16) & 0xFF;
ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
return 0;
}
static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int reg = kcontrol->private_value & 0xFF;
int shift = (kcontrol->private_value >> 8) & 0x0F;
int mask = (kcontrol->private_value >> 16) & 0xFF;
if (((lm4857_regs[reg] >> shift ) & mask) ==
ucontrol->value.integer.value[0])
return 0;
lm4857_regs[reg] &= ~ (mask << shift);
lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
lm4857_write_regs();
return 1;
}
static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
if (value)
value -= 5;
ucontrol->value.integer.value[0] = value;
return 0;
}
static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
u8 value = ucontrol->value.integer.value[0];
if (value)
value += 5;
if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
return 0;
lm4857_regs[LM4857_CTRL] &= 0xF0;
lm4857_regs[LM4857_CTRL] |= value;
lm4857_write_regs();
return 1;
}
static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Audio Out", NULL),
SND_SOC_DAPM_LINE("GSM Line Out", NULL),
SND_SOC_DAPM_LINE("GSM Line In", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Call Mic", NULL),
};
/* example machine audio_mapnections */
static const char* audio_map[][3] = {
/* Connections to the lm4857 amp */
{"Audio Out", NULL, "LOUT1"},
{"Audio Out", NULL, "ROUT1"},
/* Connections to the GSM Module */
{"GSM Line Out", NULL, "MONO1"},
{"GSM Line Out", NULL, "MONO2"},
{"RXP", NULL, "GSM Line In"},
{"RXN", NULL, "GSM Line In"},
/* Connections to Headset */
{"MIC1", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Headset Mic"},
/* Call Mic */
{"MIC2", NULL, "Mic Bias"},
{"MIC2N", NULL, "Mic Bias"},
{"Mic Bias", NULL, "Call Mic"},
/* Connect the ALC pins */
{"ACIN", NULL, "ACOP"},
{NULL, NULL, NULL},
};
static const char *lm4857_mode[] = {
"Off",
"Call Speaker",
"Stereo Speakers",
"Stereo Speakers + Headphones",
"Headphones"
};
static const struct soc_enum lm4857_mode_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
};
static const char *neo_scenarios[] = {
"Off",
"GSM Handset",
"GSM Headset",
"GSM Bluetooth",
"Speakers",
"Headphones",
"Capture Handset",
"Capture Headset",
"Capture Bluetooth"
};
static const struct soc_enum neo_scenario_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios),neo_scenarios),
};
static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
lm4857_get_mode, lm4857_set_mode),
SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
neo1973_get_scenario, neo1973_set_scenario),
SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
lm4857_get_reg, lm4857_set_reg),
SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
lm4857_get_reg, lm4857_set_reg),
};
/*
* This is an example machine initialisation for a wm8753 connected to a
* neo1973 II. It is missing logic to detect hp/mic insertions and logic
* to re-route the audio in such an event.
*/
static int neo1973_wm8753_init(struct snd_soc_codec *codec)
{
int i, err;
/* set up NC codec pins */
snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
/* set endpoints to default mode */
set_scenario_endpoints(codec, NEO_AUDIO_OFF);
/* Add neo1973 specific widgets */
for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
/* add neo1973 specific controls */
for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8753_neo1973_controls[i],
codec, NULL));
if (err < 0)
return err;
}
/* set up neo1973 specific audio path audio_mapnects */
for (i = 0; audio_map[i][0] != NULL; i++) {
snd_soc_dapm_connect_input(codec, audio_map[i][0],
audio_map[i][1], audio_map[i][2]);
}
snd_soc_dapm_sync_endpoints(codec);
return 0;
}
/*
* BT Codec DAI
*/
static struct snd_soc_cpu_dai bt_dai =
{ .name = "Bluetooth",
.id = 0,
.type = SND_SOC_DAI_PCM,
.playback = {
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 1,
.channels_max = 1,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
};
static struct snd_soc_dai_link neo1973_dai[] = {
{ /* Hifi Playback - for similatious use with voice below */
.name = "WM8753",
.stream_name = "WM8753 HiFi",
.cpu_dai = &s3c24xx_i2s_dai,
.codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
.init = neo1973_wm8753_init,
.ops = &neo1973_hifi_ops,
},
{ /* Voice via BT */
.name = "Bluetooth",
.stream_name = "Voice",
.cpu_dai = &bt_dai,
.codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
.ops = &neo1973_voice_ops,
},
};
static struct snd_soc_machine neo1973 = {
.name = "neo1973",
.dai_link = neo1973_dai,
.num_links = ARRAY_SIZE(neo1973_dai),
};
static struct wm8753_setup_data neo1973_wm8753_setup = {
.i2c_address = 0x1a,
};
static struct snd_soc_device neo1973_snd_devdata = {
.machine = &neo1973,
.platform = &s3c24xx_soc_platform,
.codec_dev = &soc_codec_dev_wm8753,
.codec_data = &neo1973_wm8753_setup,
};
static struct i2c_client client_template;
static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
/* Magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
{
int ret;
client_template.adapter = adap;
client_template.addr = addr;
i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
if (i2c == NULL)
return -ENOMEM;
ret = i2c_attach_client(i2c);
if (ret < 0) {
printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr);
goto exit_err;
}
lm4857_write_regs();
return ret;
exit_err:
kfree(i2c);
return ret;
}
static int lm4857_i2c_detach(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(client);
return 0;
}
static int lm4857_i2c_attach(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, lm4857_amp_probe);
}
/* corgi i2c codec control layer */
static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "LM4857 I2C Amp",
.owner = THIS_MODULE,
},
.id = I2C_DRIVERID_LM4857,
.attach_adapter = lm4857_i2c_attach,
.detach_client = lm4857_i2c_detach,
.command = NULL,
};
static struct i2c_client client_template = {
.name = "LM4857",
.driver = &lm4857_i2c_driver,
};
static struct platform_device *neo1973_snd_device;
static int __init neo1973_init(void)
{
int ret;
neo1973_snd_device = platform_device_alloc("soc-audio", -1);
if (!neo1973_snd_device)
return -ENOMEM;
platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
ret = platform_device_add(neo1973_snd_device);
if (ret)
platform_device_put(neo1973_snd_device);
ret = i2c_add_driver(&lm4857_i2c_driver);
if (ret != 0)
printk(KERN_ERR "can't add i2c driver");
return ret;
}
static void __exit neo1973_exit(void)
{
platform_device_unregister(neo1973_snd_device);
}
module_init(neo1973_init);
module_exit(neo1973_exit);
/* Module information */
MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,401 @@
/*
* s3c2443-ac97.c -- ALSA Soc Audio Layer
*
* (c) 2007 Wolfson Microelectronics PLC.
* Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
*
* Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Revision history
* 21st Mar 2007 Initial Version
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/regs-ac97.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-clock.h>
#include <asm/arch/audio.h>
#include <asm/dma.h>
#include <asm/arch/dma.h>
#include "s3c24xx-pcm.h"
#include "s3c24xx-ac97.h"
struct s3c24xx_ac97_info {
void __iomem *regs;
struct clk *ac97_clk;
};
static struct s3c24xx_ac97_info s3c24xx_ac97;
DECLARE_COMPLETION(ac97_completion);
static u32 codec_ready;
static DECLARE_MUTEX(ac97_mutex);
static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
u32 ac_glbctrl;
u32 ac_codec_cmd;
u32 stat, addr, data;
down(&ac97_mutex);
codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
udelay(50);
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
wait_for_completion(&ac97_completion);
stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
addr = (stat >> 16) & 0x7f;
data = (stat & 0xffff);
if (addr != reg)
printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
" rep addr = %02x\n", reg, addr);
up(&ac97_mutex);
return (unsigned short)data;
}
static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
u32 ac_glbctrl;
u32 ac_codec_cmd;
down(&ac97_mutex);
codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
udelay(50);
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
wait_for_completion(&ac97_completion);
ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
up(&ac97_mutex);
}
static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
{
u32 ac_glbctrl;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl = 0;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
}
static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
{
u32 ac_glbctrl;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl = 0;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
}
static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
{
int status;
u32 ac_glbctrl;
status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
if (status) {
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
complete(&ac97_completion);
}
return IRQ_HANDLED;
}
struct snd_ac97_bus_ops soc_ac97_ops = {
.read = s3c2443_ac97_read,
.write = s3c2443_ac97_write,
.warm_reset = s3c2443_ac97_warm_reset,
.reset = s3c2443_ac97_cold_reset,
};
static struct s3c2410_dma_client s3c2443_dma_client_out = {
.name = "AC97 PCM Stereo out"
};
static struct s3c2410_dma_client s3c2443_dma_client_in = {
.name = "AC97 PCM Stereo in"
};
static struct s3c2410_dma_client s3c2443_dma_client_micin = {
.name = "AC97 Mic Mono in"
};
static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
.client = &s3c2443_dma_client_out,
.channel = DMACH_PCM_OUT,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
.dma_size = 4,
};
static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
.client = &s3c2443_dma_client_in,
.channel = DMACH_PCM_IN,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
.dma_size = 4,
};
static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
.client = &s3c2443_dma_client_micin,
.channel = DMACH_MIC_IN,
.dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
.dma_size = 4,
};
static int s3c2443_ac97_probe(struct platform_device *pdev)
{
int ret;
u32 ac_glbctrl;
s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
if (s3c24xx_ac97.regs == NULL)
return -ENXIO;
s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
if (s3c24xx_ac97.ac97_clk == NULL) {
printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
iounmap(s3c24xx_ac97.regs);
return -ENODEV;
}
clk_enable(s3c24xx_ac97.ac97_clk);
s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl = 0;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
msleep(1);
ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
IRQF_DISABLED, "AC97", NULL);
if (ret < 0) {
printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
clk_disable(s3c24xx_ac97.ac97_clk);
clk_put(s3c24xx_ac97.ac97_clk);
iounmap(s3c24xx_ac97.regs);
}
return ret;
}
static void s3c2443_ac97_remove(struct platform_device *pdev)
{
free_irq(IRQ_S3C2443_AC97, NULL);
clk_disable(s3c24xx_ac97.ac97_clk);
clk_put(s3c24xx_ac97.ac97_clk);
iounmap(s3c24xx_ac97.regs);
}
static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
else
cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
return 0;
}
static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
{
u32 ac_glbctrl;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
switch(cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
else
ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
else
ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
break;
}
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
return 0;
}
static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
else
cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
return 0;
}
static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
int cmd)
{
u32 ac_glbctrl;
ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
switch(cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
}
writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
return 0;
}
#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
{
.name = "s3c2443-ac97",
.id = 0,
.type = SND_SOC_DAI_AC97,
.probe = s3c2443_ac97_probe,
.remove = s3c2443_ac97_remove,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
.channels_max = 2,
.rates = s3c2443_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 2,
.channels_max = 2,
.rates = s3c2443_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = s3c2443_ac97_hw_params,
.trigger = s3c2443_ac97_trigger},
},
{
.name = "pxa2xx-ac97-mic",
.id = 1,
.type = SND_SOC_DAI_AC97,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
.channels_max = 1,
.rates = s3c2443_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = s3c2443_ac97_hw_mic_params,
.trigger = s3c2443_ac97_mic_trigger,},
},
};
EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
MODULE_AUTHOR("Graeme Gregory");
MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,25 @@
/*
* s3c24xx-ac97.c -- ALSA Soc Audio Layer
*
* (c) 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
*
* Revision history
* 10th Nov 2006 Initial version.
*/
#ifndef S3C24XXAC97_H_
#define S3C24XXAC97_H_
#define AC_CMD_ADDR(x) (x << 16)
#define AC_CMD_DATA(x) (x & 0xffff)
extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
#endif /*S3C24XXAC97_H_*/

View File

@ -344,11 +344,11 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
DBG("Entered %s\n", __FUNCTION__); DBG("Entered %s\n", __FUNCTION__);
switch (div_id) { switch (div_id) {
case S3C24XX_DIV_MCLK: case S3C24XX_DIV_BCLK:
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK; reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
break; break;
case S3C24XX_DIV_BCLK: case S3C24XX_DIV_MCLK:
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS); reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
break; break;

View File

@ -0,0 +1,85 @@
/*
* smdk2443_wm9710.c -- SoC audio for smdk2443
*
* Copyright 2007 Wolfson Microelectronics PLC.
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
*
* Revision history
* 8th Mar 2007 Initial version.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "../codecs/ac97.h"
#include "s3c24xx-pcm.h"
#include "s3c24xx-ac97.h"
static struct snd_soc_machine smdk2443;
static struct snd_soc_dai_link smdk2443_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &s3c2443_ac97_dai[0],
.codec_dai = &ac97_dai,
},
};
static struct snd_soc_machine smdk2443 = {
.name = "SMDK2443",
.dai_link = smdk2443_dai,
.num_links = ARRAY_SIZE(smdk2443_dai),
};
static struct snd_soc_device smdk2443_snd_ac97_devdata = {
.machine = &smdk2443,
.platform = &s3c24xx_soc_platform,
.codec_dev = &soc_codec_dev_ac97,
};
static struct platform_device *smdk2443_snd_ac97_device;
static int __init smdk2443_init(void)
{
int ret;
smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
if (!smdk2443_snd_ac97_device)
return -ENOMEM;
platform_set_drvdata(smdk2443_snd_ac97_device,
&smdk2443_snd_ac97_devdata);
smdk2443_snd_ac97_devdata.dev = &smdk2443_snd_ac97_device->dev;
ret = platform_device_add(smdk2443_snd_ac97_device);
if (ret)
platform_device_put(smdk2443_snd_ac97_device);
return ret;
}
static void __exit smdk2443_exit(void)
{
platform_device_unregister(smdk2443_snd_ac97_device);
}
module_init(smdk2443_init);
module_exit(smdk2443_exit);
/* Module information */
MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
MODULE_LICENSE("GPL");

38
sound/soc/sh/Kconfig Normal file
View File

@ -0,0 +1,38 @@
menu "SoC Audio support for SuperH"
config SND_SOC_PCM_SH7760
tristate "SoC Audio support for Renesas SH7760"
depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
help
Enable this option for SH7760 AC97/I2S audio support.
##
## Audio unit modules
##
config SND_SOC_SH4_HAC
select AC97_BUS
select SND_SOC_AC97_BUS
select SND_AC97_CODEC
tristate
config SND_SOC_SH4_SSI
tristate
##
## Boards
##
config SND_SH7760_AC97
tristate "SH7760 AC97 sound support"
depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760
select SND_SOC_SH4_HAC
select SND_SOC_AC97_CODEC
help
This option enables generic sound support for the first
AC97 unit of the SH7760.
endmenu

14
sound/soc/sh/Makefile Normal file
View File

@ -0,0 +1,14 @@
## DMA engines
snd-soc-dma-sh7760-objs := dma-sh7760.o
obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o
## audio units found on some SH-4
snd-soc-hac-objs := hac.o
snd-soc-ssi-objs := ssi.o
obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o
obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
## boards
snd-soc-sh7760-ac97-objs := sh7760-ac97.o
obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o

354
sound/soc/sh/dma-sh7760.c Normal file
View File

@ -0,0 +1,354 @@
/*
* SH7760 ("camelot") DMABRG audio DMA unit support
*
* Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
* licensed under the terms outlined in the file COPYING at the root
* of the linux kernel sources.
*
* The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
* trigger an interrupt when one half of the programmed transfer size
* has been xmitted.
*
* FIXME: little-endian only for now
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/dmabrg.h>
/* registers and bits */
#define BRGATXSAR 0x00
#define BRGARXDAR 0x04
#define BRGATXTCR 0x08
#define BRGARXTCR 0x0C
#define BRGACR 0x10
#define BRGATXTCNT 0x14
#define BRGARXTCNT 0x18
#define ACR_RAR (1 << 18)
#define ACR_RDS (1 << 17)
#define ACR_RDE (1 << 16)
#define ACR_TAR (1 << 2)
#define ACR_TDS (1 << 1)
#define ACR_TDE (1 << 0)
/* receiver/transmitter data alignment */
#define ACR_RAM_NONE (0 << 24)
#define ACR_RAM_4BYTE (1 << 24)
#define ACR_RAM_2WORD (2 << 24)
#define ACR_TAM_NONE (0 << 8)
#define ACR_TAM_4BYTE (1 << 8)
#define ACR_TAM_2WORD (2 << 8)
struct camelot_pcm {
unsigned long mmio; /* DMABRG audio channel control reg MMIO */
unsigned int txid; /* ID of first DMABRG IRQ for this unit */
struct snd_pcm_substream *tx_ss;
unsigned long tx_period_size;
unsigned int tx_period;
struct snd_pcm_substream *rx_ss;
unsigned long rx_period_size;
unsigned int rx_period;
} cam_pcm_data[2] = {
{
.mmio = 0xFE3C0040,
.txid = DMABRGIRQ_A0TXF,
},
{
.mmio = 0xFE3C0060,
.txid = DMABRGIRQ_A1TXF,
},
};
#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x)))
/*
* set a minimum of 16kb per period, to avoid interrupt-"storm" and
* resulting skipping. In general, the bigger the minimum size, the
* better for overall system performance. (The SH7760 is a puny CPU
* with a slow SDRAM interface and poor internal bus bandwidth,
* *especially* when the LCDC is active). The minimum for the DMAC
* is 8 bytes; 16kbytes are enough to get skip-free playback of a
* 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
* reasonable responsiveness in MPlayer.
*/
#define DMABRG_PERIOD_MIN 16 * 1024
#define DMABRG_PERIOD_MAX 0x03fffffc
#define DMABRG_PREALLOC_BUFFER 32 * 1024
#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024
/* support everything the SSI supports */
#define DMABRG_RATES \
SNDRV_PCM_RATE_8000_192000
#define DMABRG_FMTS \
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
static struct snd_pcm_hardware camelot_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = DMABRG_FMTS,
.rates = DMABRG_RATES,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 8, /* max of the SSI */
.buffer_bytes_max = DMABRG_PERIOD_MAX,
.period_bytes_min = DMABRG_PERIOD_MIN,
.period_bytes_max = DMABRG_PERIOD_MAX / 2,
.periods_min = 2,
.periods_max = 2,
.fifo_size = 128,
};
static void camelot_txdma(void *data)
{
struct camelot_pcm *cam = data;
cam->tx_period ^= 1;
snd_pcm_period_elapsed(cam->tx_ss);
}
static void camelot_rxdma(void *data)
{
struct camelot_pcm *cam = data;
cam->rx_period ^= 1;
snd_pcm_period_elapsed(cam->rx_ss);
}
static int camelot_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
int ret, dmairq;
snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
/* DMABRG buffer half/full events */
dmairq = (recv) ? cam->txid + 2 : cam->txid;
if (recv) {
cam->rx_ss = substream;
ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
if (unlikely(ret)) {
pr_debug("audio unit %d irqs already taken!\n",
rtd->dai->cpu_dai->id);
return -EBUSY;
}
(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
} else {
cam->tx_ss = substream;
ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
if (unlikely(ret)) {
pr_debug("audio unit %d irqs already taken!\n",
rtd->dai->cpu_dai->id);
return -EBUSY;
}
(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
}
return 0;
}
static int camelot_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
int dmairq;
dmairq = (recv) ? cam->txid + 2 : cam->txid;
if (recv)
cam->rx_ss = NULL;
else
cam->tx_ss = NULL;
dmabrg_free_irq(dmairq + 1);
dmabrg_free_irq(dmairq);
return 0;
}
static int camelot_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
int ret;
ret = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
if (recv) {
cam->rx_period_size = params_period_bytes(hw_params);
cam->rx_period = 0;
} else {
cam->tx_period_size = params_period_bytes(hw_params);
cam->tx_period = 0;
}
return 0;
}
static int camelot_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int camelot_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
pr_debug("PCM data: addr 0x%08ulx len %d\n",
(u32)runtime->dma_addr, runtime->dma_bytes);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
BRGREG(BRGATXTCR) = runtime->dma_bytes;
} else {
BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
BRGREG(BRGARXTCR) = runtime->dma_bytes;
}
return 0;
}
static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
{
unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
/* start DMABRG engine: XFER start, auto-addr-reload */
BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
}
static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
{
unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
/* forcibly terminate data transmission */
BRGREG(BRGACR) = acr | ACR_TDS;
}
static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
{
unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
/* start DMABRG engine: recv start, auto-reload */
BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
}
static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
{
unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
/* forcibly terminate data receiver */
BRGREG(BRGACR) = acr | ACR_RDS;
}
static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (recv)
dmabrg_rec_dma_start(cam);
else
dmabrg_play_dma_start(cam);
break;
case SNDRV_PCM_TRIGGER_STOP:
if (recv)
dmabrg_rec_dma_stop(cam);
else
dmabrg_play_dma_stop(cam);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
unsigned long pos;
/* cannot use the DMABRG pointer register: under load, by the
* time ALSA comes around to read the register, it is already
* far ahead (or worse, already done with the fragment) of the
* position at the time the IRQ was triggered, which results in
* fast-playback sound in my test application (ScummVM)
*/
if (recv)
pos = cam->rx_period ? cam->rx_period_size : 0;
else
pos = cam->tx_period ? cam->tx_period_size : 0;
return bytes_to_frames(runtime, pos);
}
static struct snd_pcm_ops camelot_pcm_ops = {
.open = camelot_pcm_open,
.close = camelot_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = camelot_hw_params,
.hw_free = camelot_hw_free,
.prepare = camelot_prepare,
.trigger = camelot_trigger,
.pointer = camelot_pos,
};
static void camelot_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int camelot_pcm_new(struct snd_card *card,
struct snd_soc_codec_dai *dai,
struct snd_pcm *pcm)
{
/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
* in MMAP mode (i.e. aplay -M)
*/
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX);
return 0;
}
struct snd_soc_platform sh7760_soc_platform = {
.name = "sh7760-pcm",
.pcm_ops = &camelot_pcm_ops,
.pcm_new = camelot_pcm_new,
.pcm_free = camelot_pcm_free,
};
EXPORT_SYMBOL_GPL(sh7760_soc_platform);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

322
sound/soc/sh/hac.c Normal file
View File

@ -0,0 +1,322 @@
/*
* Hitachi Audio Controller (AC97) support for SH7760/SH7780
*
* Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
* licensed under the terms outlined in the file COPYING at the root
* of the linux kernel sources.
*
* dont forget to set IPSEL/OMSEL register bits (in your board code) to
* enable HAC output pins!
*/
/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
* the FIRST can be used since ASoC does not pass any information to the
* ac97_read/write() functions regarding WHICH unit to use. You'll have
* to edit the code a bit to use the other AC97 unit. --mlau
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
/* regs and bits */
#define HACCR 0x08
#define HACCSAR 0x20
#define HACCSDR 0x24
#define HACPCML 0x28
#define HACPCMR 0x2C
#define HACTIER 0x50
#define HACTSR 0x54
#define HACRIER 0x58
#define HACRSR 0x5C
#define HACACR 0x60
#define CR_CR (1 << 15) /* "codec-ready" indicator */
#define CR_CDRT (1 << 11) /* cold reset */
#define CR_WMRT (1 << 10) /* warm reset */
#define CR_B9 (1 << 9) /* the mysterious "bit 9" */
#define CR_ST (1 << 5) /* AC97 link start bit */
#define CSAR_RD (1 << 19) /* AC97 data read bit */
#define CSAR_WR (0)
#define TSR_CMDAMT (1 << 31)
#define TSR_CMDDMT (1 << 30)
#define RSR_STARY (1 << 22)
#define RSR_STDRY (1 << 21)
#define ACR_DMARX16 (1 << 30)
#define ACR_DMATX16 (1 << 29)
#define ACR_TX12ATOM (1 << 26)
#define ACR_DMARX20 ((1 << 24) | (1 << 22))
#define ACR_DMATX20 ((1 << 23) | (1 << 21))
#define CSDR_SHIFT 4
#define CSDR_MASK (0xffff << CSDR_SHIFT)
#define CSAR_SHIFT 12
#define CSAR_MASK (0x7f << CSAR_SHIFT)
#define AC97_WRITE_RETRY 1
#define AC97_READ_RETRY 5
/* manual-suggested AC97 codec access timeouts (us) */
#define TMO_E1 500 /* 21 < E1 < 1000 */
#define TMO_E2 13 /* 13 < E2 */
#define TMO_E3 21 /* 21 < E3 */
#define TMO_E4 500 /* 21 < E4 < 1000 */
struct hac_priv {
unsigned long mmio; /* HAC base address */
} hac_cpu_data[] = {
#if defined(CONFIG_CPU_SUBTYPE_SH7760)
{
.mmio = 0xFE240000,
},
{
.mmio = 0xFE250000,
},
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
{
.mmio = 0xFFE40000,
},
#else
#error "Unsupported SuperH SoC"
#endif
};
#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg)))
/*
* AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
*/
static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
unsigned short *v)
{
unsigned int to1, to2, i;
unsigned short adr;
for (i = 0; i < AC97_READ_RETRY; ++i) {
*v = 0;
/* wait for HAC to receive something from the codec */
for (to1 = TMO_E4;
to1 && !(HACREG(HACRSR) & RSR_STARY);
--to1)
udelay(1);
for (to2 = TMO_E4;
to2 && !(HACREG(HACRSR) & RSR_STDRY);
--to2)
udelay(1);
if (!to1 && !to2)
return 0; /* codec comm is down */
adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
*v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
if (r == adr)
break;
/* manual says: wait at least 21 usec before retrying */
udelay(21);
}
HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
return (i < AC97_READ_RETRY);
}
static unsigned short hac_read_codec_aux(struct hac_priv *hac,
unsigned short reg)
{
unsigned short val;
unsigned int i, to;
for (i = 0; i < AC97_READ_RETRY; i++) {
/* send_read_request */
local_irq_disable();
HACREG(HACTSR) &= ~(TSR_CMDAMT);
HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
local_irq_enable();
for (to = TMO_E3;
to && !(HACREG(HACTSR) & TSR_CMDAMT);
--to)
udelay(1);
HACREG(HACTSR) &= ~TSR_CMDAMT;
val = 0;
if (hac_get_codec_data(hac, reg, &val) != 0)
break;
}
if (i == AC97_READ_RETRY)
return ~0;
return val;
}
static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
int unit_id = 0 /* ac97->private_data */;
struct hac_priv *hac = &hac_cpu_data[unit_id];
unsigned int i, to;
/* write_codec_aux */
for (i = 0; i < AC97_WRITE_RETRY; i++) {
/* send_write_request */
local_irq_disable();
HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
HACREG(HACCSDR) = (val << CSDR_SHIFT);
HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
local_irq_enable();
/* poll-wait for CMDAMT and CMDDMT */
for (to = TMO_E1;
to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
--to)
udelay(1);
HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
if (to)
break;
/* timeout, try again */
}
}
static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
int unit_id = 0 /* ac97->private_data */;
struct hac_priv *hac = &hac_cpu_data[unit_id];
return hac_read_codec_aux(hac, reg);
}
static void hac_ac97_warmrst(struct snd_ac97 *ac97)
{
int unit_id = 0 /* ac97->private_data */;
struct hac_priv *hac = &hac_cpu_data[unit_id];
unsigned int tmo;
HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9;
msleep(10);
HACREG(HACCR) = CR_ST | CR_B9;
for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--)
udelay(1);
if (!tmo)
printk(KERN_INFO "hac: reset: AC97 link down!\n");
/* settings this bit lets us have a conversation with codec */
HACREG(HACACR) |= ACR_TX12ATOM;
}
static void hac_ac97_coldrst(struct snd_ac97 *ac97)
{
int unit_id = 0 /* ac97->private_data */;
struct hac_priv *hac;
hac = &hac_cpu_data[unit_id];
HACREG(HACCR) = 0;
HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9;
msleep(10);
hac_ac97_warmrst(ac97);
}
struct snd_ac97_bus_ops soc_ac97_ops = {
.read = hac_ac97_read,
.write = hac_ac97_write,
.reset = hac_ac97_coldrst,
.warm_reset = hac_ac97_warmrst,
};
EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int hac_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
switch (params->msbits) {
case 16:
HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16;
HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20;
break;
case 20:
HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16;
HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20;
break;
default:
pr_debug("hac: invalid depth %d bit\n", params->msbits);
return -EINVAL;
break;
}
return 0;
}
#define AC97_RATES \
SNDRV_PCM_RATE_8000_192000
#define AC97_FMTS \
SNDRV_PCM_FMTBIT_S16_LE
struct snd_soc_cpu_dai sh4_hac_dai[] = {
{
.name = "HAC0",
.id = 0,
.type = SND_SOC_DAI_AC97,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.ops = {
.hw_params = hac_hw_params,
},
},
#ifdef CONFIG_CPU_SUBTYPE_SH7760
{
.name = "HAC1",
.id = 1,
.type = SND_SOC_DAI_AC97,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
.channels_min = 2,
.channels_max = 2,
},
.ops = {
.hw_params = hac_hw_params,
},
},
#endif
};
EXPORT_SYMBOL_GPL(sh4_hac_dai);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

View File

@ -0,0 +1,92 @@
/*
* Generic AC97 sound support for SH7760
*
* (c) 2007 Manuel Lauss
*
* Licensed under the GPLv2.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/io.h>
#include "../codecs/ac97.h"
#define IPSEL 0xFE400034
/* platform specific structs can be declared here */
extern struct snd_soc_cpu_dai sh4_hac_dai[2];
extern struct snd_soc_platform sh7760_soc_platform;
static int machine_init(struct snd_soc_codec *codec)
{
snd_soc_dapm_sync_endpoints(codec);
return 0;
}
static struct snd_soc_dai_link sh7760_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &sh4_hac_dai[0], /* HAC0 */
.codec_dai = &ac97_dai,
.init = machine_init,
.ops = NULL,
};
static struct snd_soc_machine sh7760_ac97_soc_machine = {
.name = "SH7760 AC97",
.dai_link = &sh7760_ac97_dai,
.num_links = 1,
};
static struct snd_soc_device sh7760_ac97_snd_devdata = {
.machine = &sh7760_ac97_soc_machine,
.platform = &sh7760_soc_platform,
.codec_dev = &soc_codec_dev_ac97,
};
static struct platform_device *sh7760_ac97_snd_device;
static int __init sh7760_ac97_init(void)
{
int ret;
unsigned short ipsel;
/* enable both AC97 controllers in pinmux reg */
ipsel = ctrl_inw(IPSEL);
ctrl_outw(ipsel | (3 << 10), IPSEL);
ret = -ENOMEM;
sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1);
if (!sh7760_ac97_snd_device)
goto out;
platform_set_drvdata(sh7760_ac97_snd_device,
&sh7760_ac97_snd_devdata);
sh7760_ac97_snd_devdata.dev = &sh7760_ac97_snd_device->dev;
ret = platform_device_add(sh7760_ac97_snd_device);
if (ret)
platform_device_put(sh7760_ac97_snd_device);
out:
return ret;
}
static void __exit sh7760_ac97_exit(void)
{
platform_device_unregister(sh7760_ac97_snd_device);
}
module_init(sh7760_ac97_init);
module_exit(sh7760_ac97_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

400
sound/soc/sh/ssi.c Normal file
View File

@ -0,0 +1,400 @@
/*
* Serial Sound Interface (I2S) support for SH7760/SH7780
*
* Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
*
* licensed under the terms outlined in the file COPYING at the root
* of the linux kernel sources.
*
* dont forget to set IPSEL/OMSEL register bits (in your board code) to
* enable SSI output pins!
*/
/*
* LIMITATIONS:
* The SSI unit has only one physical data line, so full duplex is
* impossible. This can be remedied on the SH7760 by using the
* other SSI unit for recording; however the SH7780 has only 1 SSI
* unit, and its pins are shared with the AC97 unit, among others.
*
* FEATURES:
* The SSI features "compressed mode": in this mode it continuously
* streams PCM data over the I2S lines and uses LRCK as a handshake
* signal. Can be used to send compressed data (AC3/DTS) to a DSP.
* The number of bits sent over the wire in a frame can be adjusted
* and can be independent from the actual sample bit depth. This is
* useful to support TDM mode codecs like the AD1939 which have a
* fixed TDM slot size, regardless of sample resolution.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <asm/io.h>
#define SSICR 0x00
#define SSISR 0x04
#define CR_DMAEN (1 << 28)
#define CR_CHNL_SHIFT 22
#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT)
#define CR_DWL_SHIFT 19
#define CR_DWL_MASK (7 << CR_DWL_SHIFT)
#define CR_SWL_SHIFT 16
#define CR_SWL_MASK (7 << CR_SWL_SHIFT)
#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */
#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */
#define CR_SCKP (1 << 13) /* I2Sclock polarity */
#define CR_SWSP (1 << 12) /* LRCK polarity */
#define CR_SPDP (1 << 11)
#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */
#define CR_PDTA (1 << 9) /* fifo data alignment */
#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */
#define CR_BREN (1 << 7) /* clock gating in burst mode */
#define CR_CKDIV_SHIFT 4
#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */
#define CR_MUTE (1 << 3) /* SSI mute */
#define CR_CPEN (1 << 2) /* compressed mode */
#define CR_TRMD (1 << 1) /* transmit/receive select */
#define CR_EN (1 << 0) /* enable SSI */
#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg)))
struct ssi_priv {
unsigned long mmio;
unsigned long sysclk;
int inuse;
} ssi_cpu_data[] = {
#if defined(CONFIG_CPU_SUBTYPE_SH7760)
{
.mmio = 0xFE680000,
},
{
.mmio = 0xFE690000,
},
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
{
.mmio = 0xFFE70000,
},
#else
#error "Unsupported SuperH SoC"
#endif
};
/*
* track usage of the SSI; it is simplex-only so prevent attempts of
* concurrent playback + capture. FIXME: any locking required?
*/
static int ssi_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
if (ssi->inuse) {
pr_debug("ssi: already in use!\n");
return -EBUSY;
} else
ssi->inuse = 1;
return 0;
}
static void ssi_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
ssi->inuse = 0;
}
static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
SSIREG(SSICR) |= CR_DMAEN | CR_EN;
break;
case SNDRV_PCM_TRIGGER_STOP:
SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
break;
default:
return -EINVAL;
}
return 0;
}
static int ssi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
unsigned long ssicr = SSIREG(SSICR);
unsigned int bits, channels, swl, recv, i;
channels = params_channels(params);
bits = params->msbits;
recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr);
pr_debug("bits: %d channels: %d\n", bits, channels);
ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
CR_SWL_MASK);
/* direction (send/receive) */
if (!recv)
ssicr |= CR_TRMD; /* transmit */
/* channels */
if ((channels < 2) || (channels > 8) || (channels & 1)) {
pr_debug("ssi: invalid number of channels\n");
return -EINVAL;
}
ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
/* DATA WORD LENGTH (DWL): databits in audio sample */
i = 0;
switch (bits) {
case 32: ++i;
case 24: ++i;
case 22: ++i;
case 20: ++i;
case 18: ++i;
case 16: ++i;
ssicr |= i << CR_DWL_SHIFT;
case 8: break;
default:
pr_debug("ssi: invalid sample width\n");
return -EINVAL;
}
/*
* SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
* wires. This is usually bits_per_sample x channels/2; i.e. in
* Stereo mode the SWL equals DWL. SWL can be bigger than the
* product of (channels_per_slot x samplebits), e.g. for codecs
* like the AD1939 which only accept 32bit wide TDM slots. For
* "standard" I2S operation we set SWL = chans / 2 * DWL here.
* Waiting for ASoC to get TDM support ;-)
*/
if ((bits > 16) && (bits <= 24)) {
bits = 24; /* these are padded by the SSI */
/*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
}
i = 0;
swl = (bits * channels) / 2;
switch (swl) {
case 256: ++i;
case 128: ++i;
case 64: ++i;
case 48: ++i;
case 32: ++i;
case 16: ++i;
ssicr |= i << CR_SWL_SHIFT;
case 8: break;
default:
pr_debug("ssi: invalid system word length computed\n");
return -EINVAL;
}
SSIREG(SSICR) = ssicr;
pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
return 0;
}
static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
ssi->sysclk = freq;
return 0;
}
/*
* This divider is used to generate the SSI_SCK (I2S bitclock) from the
* clock at the HAC_BIT_CLK ("oversampling clock") pin.
*/
static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr;
int i;
i = 0;
ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
switch (div) {
case 16: ++i;
case 8: ++i;
case 4: ++i;
case 2: ++i;
SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
case 1: break;
default:
pr_debug("ssi: invalid sck divider %d\n", div);
return -EINVAL;
}
return 0;
}
static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
{
struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
unsigned long ssicr = SSIREG(SSICR);
pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr);
ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
CR_SWS_MASTER | CR_SCK_MASTER);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
break;
case SND_SOC_DAIFMT_RIGHT_J:
ssicr |= CR_DEL | CR_PDTA;
break;
case SND_SOC_DAIFMT_LEFT_J:
ssicr |= CR_DEL;
break;
default:
pr_debug("ssi: unsupported format\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
case SND_SOC_DAIFMT_CONT:
break;
case SND_SOC_DAIFMT_GATED:
ssicr |= CR_BREN;
break;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
ssicr |= CR_SCKP; /* sample data at low clkedge */
break;
case SND_SOC_DAIFMT_NB_IF:
ssicr |= CR_SCKP | CR_SWSP;
break;
case SND_SOC_DAIFMT_IB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
ssicr |= CR_SWSP; /* word select starts low */
break;
default:
pr_debug("ssi: invalid inversion\n");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
break;
case SND_SOC_DAIFMT_CBS_CFM:
ssicr |= CR_SCK_MASTER;
break;
case SND_SOC_DAIFMT_CBM_CFS:
ssicr |= CR_SWS_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
break;
default:
pr_debug("ssi: invalid master/slave configuration\n");
return -EINVAL;
}
SSIREG(SSICR) = ssicr;
pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
return 0;
}
/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
* Master mode, so really this is board specific; the SSI can do any
* rate with the right bitclk and divider settings.
*/
#define SSI_RATES \
SNDRV_PCM_RATE_8000_192000
/* the SSI can do 8-32 bit samples, with 8 possible channels */
#define SSI_FMTS \
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
struct snd_soc_cpu_dai sh4_ssi_dai[] = {
{
.name = "SSI0",
.id = 0,
.type = SND_SOC_DAI_I2S,
.playback = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
.channels_min = 2,
.channels_max = 8,
},
.capture = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
.channels_min = 2,
.channels_max = 8,
},
.ops = {
.startup = ssi_startup,
.shutdown = ssi_shutdown,
.trigger = ssi_trigger,
.hw_params = ssi_hw_params,
},
.dai_ops = {
.set_sysclk = ssi_set_sysclk,
.set_clkdiv = ssi_set_clkdiv,
.set_fmt = ssi_set_fmt,
},
},
#ifdef CONFIG_CPU_SUBTYPE_SH7760
{
.name = "SSI1",
.id = 1,
.type = SND_SOC_DAI_I2S,
.playback = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
.channels_min = 2,
.channels_max = 8,
},
.capture = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
.channels_min = 2,
.channels_max = 8,
},
.ops = {
.startup = ssi_startup,
.shutdown = ssi_shutdown,
.trigger = ssi_trigger,
.hw_params = ssi_hw_params,
},
.dai_ops = {
.set_sysclk = ssi_set_sysclk,
.set_clkdiv = ssi_set_clkdiv,
.set_fmt = ssi_set_fmt,
},
},
#endif
};
EXPORT_SYMBOL_GPL(sh4_ssi_dai);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

View File

@ -2350,7 +2350,9 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
return 1; return 1;
break; break;
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
return 1; if (device_setup[chip->index] == 0x00 ||
fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
return 1;
} }
return 0; return 0;
} }
@ -2530,7 +2532,18 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
* but we give normal PCM format to get the existing * but we give normal PCM format to get the existing
* apps working... * apps working...
*/ */
pcm_format = SNDRV_PCM_FORMAT_S16_LE; switch (chip->usb_id) {
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
if (device_setup[chip->index] == 0x00 &&
fp->altsetting == 6)
pcm_format = SNDRV_PCM_FORMAT_S16_BE;
else
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
break;
default:
pcm_format = SNDRV_PCM_FORMAT_S16_LE;
}
} else { } else {
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt); pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
if (pcm_format < 0) if (pcm_format < 0)
@ -3251,6 +3264,11 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
int iface, int altno) int iface, int altno)
{ {
/* Reset ALL ifaces to 0 altsetting.
* Call it for every possible altsetting of every interface.
*/
usb_set_interface(chip->dev, iface, 0);
if (device_setup[chip->index] & AUDIOPHILE_SET) { if (device_setup[chip->index] & AUDIOPHILE_SET) {
if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS) if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
&& altno != 6) && altno != 6)

View File

@ -52,6 +52,24 @@
.bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
}, },
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
.idVendor = 0x046d,
.idProduct = 0x08ae,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS,
.idVendor = 0x046d,
.idProduct = 0x08c6,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
},
{ {
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_CLASS |
@ -1051,7 +1069,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_MIDI_STANDARD_INTERFACE .type = QUIRK_MIDI_STANDARD_INTERFACE
} }
}, },
/* TODO: add Roland EXR support */ {
USB_DEVICE(0x0582, 0x0060),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.vendor_name = "Roland",
.product_name = "EXR Series",
.ifnum = 0,
.type = QUIRK_MIDI_STANDARD_INTERFACE
}
},
{ {
/* has ID 0x0067 when not in "Advanced Driver" mode */ /* has ID 0x0067 when not in "Advanced Driver" mode */
USB_DEVICE(0x0582, 0x0065), USB_DEVICE(0x0582, 0x0065),
@ -1094,6 +1120,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
} }
} }
}, },
{
USB_DEVICE(0x582, 0x00a6),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.vendor_name = "Roland",
.product_name = "Juno-G",
.ifnum = 0,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const struct snd_usb_midi_endpoint_info) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
}
},
{ /* { /*
* This quirk is for the "Advanced" modes of the Edirol UA-25. * This quirk is for the "Advanced" modes of the Edirol UA-25.
* If the switch is not in an advanced setting, the UA-25 has * If the switch is not in an advanced setting, the UA-25 has
@ -1230,6 +1269,37 @@ YAMAHA_DEVICE(0x7010, "UB99"),
} }
}, },
/* TODO: add Edirol MD-P1 support */ /* TODO: add Edirol MD-P1 support */
{
/* Roland SH-201 */
USB_DEVICE(0x0582, 0x00ad),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
.vendor_name = "Roland",
.product_name = "SH-201",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = (const struct snd_usb_audio_quirk[]) {
{
.ifnum = 0,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 1,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const struct snd_usb_midi_endpoint_info) {
.out_cables = 0x0001,
.in_cables = 0x0001
}
},
{
.ifnum = -1
}
}
}
},
/* Guillemot devices */ /* Guillemot devices */
{ {

View File

@ -935,10 +935,9 @@ static struct snd_pcm_ops snd_usX2Y_pcm_ops =
*/ */
static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream) static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream)
{ {
if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) { kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]); usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
}
kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]); kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL; usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
} }