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:
commit
7f46e6ca01
10
CREDITS
10
CREDITS
|
@ -2212,13 +2212,13 @@ S: 2300 Copenhagen S
|
|||
S: Denmark
|
||||
|
||||
N: Claudio S. Matsuoka
|
||||
E: claudio@conectiva.com
|
||||
E: claudio@helllabs.org
|
||||
E: cmatsuoka@gmail.com
|
||||
E: claudio@mandriva.com
|
||||
W: http://helllabs.org/~claudio
|
||||
D: V4L, OV511 driver hacks
|
||||
D: V4L, OV511 and HDA-codec hacks
|
||||
S: Conectiva S.A.
|
||||
S: R. Tocantins 89
|
||||
S: 80050-430 Curitiba PR
|
||||
S: Souza Naves 1250
|
||||
S: 80050-040 Curitiba PR
|
||||
S: Brazil
|
||||
|
||||
N: Heinz Mauelshagen
|
||||
|
|
|
@ -467,7 +467,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
above explicitly.
|
||||
|
||||
The power-management is supported.
|
||||
|
||||
|
||||
Module snd-cs5530
|
||||
_________________
|
||||
|
||||
Module for Cyrix/NatSemi Geode 5530 chip.
|
||||
|
||||
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
|
||||
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
|
||||
codecs (for debugging only)
|
||||
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)
|
||||
fujitsu Fujitsu S7020
|
||||
acer Acer TravelMate
|
||||
will Will laptops (PB V7900)
|
||||
replacer Replacer 672V
|
||||
basic fixed pin assignment (old default model)
|
||||
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-d7000 HP BPC D7000
|
||||
benq Benq ED8
|
||||
benq-t31 Benq T31
|
||||
hippo Hippo (ATI) with jack detection, Sony UX-90s
|
||||
hippo_1 Hippo (Benq) with jack detection
|
||||
sony-assamd Sony ASSAMD
|
||||
basic fixed pin assignment w/o SPDIF
|
||||
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
|
||||
3stack-dig 3-jack with SPDIF I/O
|
||||
6stack-dig 6-jack digital with SPDIF I/O
|
||||
arima Arima W820Di1
|
||||
macpro MacPro support
|
||||
imac24 iMac 24'' with jack detection
|
||||
w2jc ASUS W2JC
|
||||
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
|
||||
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
|
||||
medion Medion Laptops
|
||||
medion-md2 Medion MD2
|
||||
targa-dig Targa/MSI
|
||||
targa-2ch-dig Targs/MSI with 2-channel
|
||||
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)
|
||||
|
||||
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
|
||||
6stack-dig 6-jack with SPDIF OUT
|
||||
3stack-660 3-jack (for ALC660VD)
|
||||
3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
|
||||
lenovo Lenovo 3000 C200
|
||||
dallas Dallas laptops
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
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
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
AD1882
|
||||
3stack 3-stack mode (default)
|
||||
6stack 6-stack mode
|
||||
|
||||
AD1884
|
||||
N/A
|
||||
|
||||
AD1981
|
||||
basic 3-jack (default)
|
||||
hp HP nx6320
|
||||
thinkpad Lenovo Thinkpad T60/X60/Z60
|
||||
toshiba Toshiba U205
|
||||
|
||||
AD1983
|
||||
N/A
|
||||
|
||||
AD1984
|
||||
basic default configuration
|
||||
thinkpad Lenovo Thinkpad T61/X61
|
||||
|
||||
AD1986A
|
||||
6stack 6-jack, separate surrounds (default)
|
||||
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
|
||||
3stack D945 3stack
|
||||
5stack D945 5stack + SPDIF
|
||||
macmini Intel Mac Mini
|
||||
macbook Intel Mac Book
|
||||
macbook-pro-v1 Intel Mac Book Pro 1st generation
|
||||
macbook-pro Intel Mac Book Pro 2nd generation
|
||||
imac-intel Intel iMac
|
||||
dell Dell XPS M1210
|
||||
intel-mac-v1 Intel Mac Type 1
|
||||
intel-mac-v2 Intel Mac Type 2
|
||||
intel-mac-v3 Intel Mac Type 3
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
Module snd-hdsp
|
||||
|
|
|
@ -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>
|
||||
|
@ -6,8 +6,19 @@
|
|||
This document is a guide to using the M-Audio Audiophile USB (tm) device with
|
||||
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
|
||||
==========================================
|
||||
|
||||
This part is a reminder of important facts about the functions and limitations
|
||||
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:
|
||||
* sample depth of 16 or 24 bits
|
||||
* sample rate from 8kHz to 96kHz
|
||||
* Two ports can't use different sample depths at the same time. Moreover, the
|
||||
Audiophile USB documentation gives the following Warning: "Please exit any
|
||||
audio application running before switching between bit depths"
|
||||
* Two interfaces can't use different sample depths at the same time.
|
||||
Moreover, the Audiophile USB documentation gives the following Warning:
|
||||
"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
|
||||
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
|
||||
* 24-bit/48kHz ==> 4 channels in/2 channels out,
|
||||
or 2 channels in/4 channels out
|
||||
* 24-bit/48kHz ==> 4 channels in + 2 channels out,
|
||||
or 2 channels in + 4 channels out
|
||||
- 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
|
||||
|
||||
Important facts about the Digital interface:
|
||||
|
@ -52,44 +63,56 @@ source is connected
|
|||
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:
|
||||
* snd-usb-audio
|
||||
* snd-seq-midi
|
||||
|
||||
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
|
||||
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
|
||||
"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
|
||||
capabilities at startup and enable all functions inside the device (including
|
||||
all ports at any supported sample rates and sample depths). This approach
|
||||
has the advantage to let the driver easily switch from sample rates/depths
|
||||
automatically according to the need of the application claiming the device.
|
||||
The default behavior of the snd-usb-audio driver is to list the device
|
||||
capabilities at startup and activate the required mode when required
|
||||
by the applications: for instance if the user is recording in a
|
||||
24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
|
||||
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
|
||||
following way (I suppose the device's index is 1):
|
||||
This approach has the advantage to let the driver automatically switch from sample
|
||||
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,1 is Do in playback and Ai in capture
|
||||
* 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
|
||||
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
|
||||
compliant and thus uses S16_LE.
|
||||
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
|
||||
24-bits depth mode.
|
||||
|
||||
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:
|
||||
* 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
|
||||
* playing a S16_BE encoded raw file to the Do port
|
||||
% 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.
|
||||
|
||||
2.2.2 - Advanced module setup
|
||||
3.2 - Advanced module setup
|
||||
---------------------------
|
||||
|
||||
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
|
||||
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
|
||||
gain).
|
||||
from the Ai interface sounds distorted (as if boosted with an excessive high
|
||||
volume gain).
|
||||
|
||||
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
|
||||
specify:
|
||||
|
@ -121,33 +148,57 @@ specify:
|
|||
* the sample rate
|
||||
* whether the Di port is used or not
|
||||
|
||||
Here is a list of supported device_setup values for this device:
|
||||
* device_setup=0x00 (or omitted)
|
||||
- Alsa driver default mode
|
||||
- maintains backward compatibility with setups that do not use this
|
||||
parameter by not introducing any change
|
||||
- results sometimes in corrupted sound as described earlier
|
||||
When initialized with "device_setup=0x00", the snd-usb-audio module has
|
||||
the same behaviour as when the parameter is omitted (see paragraph "Default
|
||||
Alsa driver mode" above)
|
||||
|
||||
Others modes are described in the following subsections.
|
||||
|
||||
3.2.1.1 - 16-bit modes
|
||||
|
||||
The two supported modes are:
|
||||
|
||||
* device_setup=0x01
|
||||
- 16bits 48kHz mode with Di disabled
|
||||
- Ai,Ao,Do can be used at the same time
|
||||
- hw:1,0 is not available in capture mode
|
||||
- hw:1,2 is not available
|
||||
|
||||
* device_setup=0x11
|
||||
- 16bits 48kHz mode with Di enabled
|
||||
- Ai,Ao,Di,Do can be used at the same time
|
||||
- hw:1,0 is available in capture mode
|
||||
- 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
|
||||
- 24bits 48kHz mode with Di disabled
|
||||
- Ai,Ao,Do can be used at the same time
|
||||
- hw:1,0 is not available in capture mode
|
||||
- hw:1,2 is not available
|
||||
|
||||
* device_setup=0x19
|
||||
- 24bits 48kHz mode with Di enabled
|
||||
- 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
|
||||
connected to Di
|
||||
- hw:1,2 is not available
|
||||
|
||||
* device_setup=0x0D or 0x10
|
||||
- 24bits 96kHz mode
|
||||
- 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
|
||||
- hw:1,0 is available in captured mode
|
||||
- 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
|
||||
- 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
|
||||
|
||||
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:
|
||||
|
||||
* By manually probing the device (as root):
|
||||
# modprobe -r snd-usb-audio
|
||||
# modprobe snd-usb-audio index=1 device_setup=0x09
|
||||
|
||||
* Or while configuring the modules options in your modules configuration file
|
||||
- For Fedora distributions, edit the /etc/modprobe.conf file:
|
||||
alias snd-card-1 snd-usb-audio
|
||||
options snd-usb-audio index=1 device_setup=0x09
|
||||
|
||||
IMPORTANT NOTE WHEN SWITCHING CONFIGURATION:
|
||||
-------------------------------------------
|
||||
* You may need to _first_ initialize the module with the correct device_setup
|
||||
parameter and _only_after_ turn on the Audiophile USB device
|
||||
* This is especially true when switching the sample depth:
|
||||
CAUTION when initializaing the device
|
||||
-------------------------------------
|
||||
|
||||
* Correct initialization on the device requires that device_setup is given to
|
||||
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
|
||||
- de-register the snd-usb-audio module (modprobe -r)
|
||||
- change the device_setup parameter by changing the device_setup
|
||||
option in /etc/modprobe.conf
|
||||
- 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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
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
|
||||
for this device with Alsa is to use the Alsa "plug" converter.
|
||||
4.1 - Direct support in Jackd
|
||||
-----------------------------
|
||||
|
||||
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
|
||||
capture channels from Ai:
|
||||
% jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
|
||||
|
||||
|
||||
However you may see the following warning message:
|
||||
"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.
|
||||
Consider using a hardware device instead rather than using the plug layer."
|
||||
|
||||
3.2 - Patching alsa to use direct pcm device
|
||||
--------------------------------------------
|
||||
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
|
||||
4.3 - Getting 2 input and/or output interfaces in Jack
|
||||
------------------------------------------------------
|
||||
|
||||
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
|
||||
* The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
|
||||
(and optionally hw:1,2)
|
||||
|
||||
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.
|
||||
|
||||
|
@ -348,13 +432,11 @@ It is related to another device (ice1712) but can be adapted to suit
|
|||
the Audiophile USB.
|
||||
|
||||
Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
|
||||
* patching Jack with the previously mentioned "Big Endian" patch
|
||||
* patching Jackd with the MMAP_COMPLEX patch (see the ice1712 page)
|
||||
* patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
|
||||
* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
|
||||
* (maybe) 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
|
||||
file
|
||||
* start jackd with this device
|
||||
|
||||
I had no success in testing this for now, but this may be due to my OS
|
||||
configuration. If you have any success with this kind of setup, please
|
||||
drop me an email.
|
||||
I had no success in testing this for now, if you have any success with this kind
|
||||
of setup, please drop me an email.
|
||||
|
|
|
@ -278,6 +278,21 @@ current mixer configuration by reading and writing the whole file
|
|||
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
|
||||
====================
|
||||
|
||||
|
|
|
@ -115,9 +115,10 @@
|
|||
#define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */
|
||||
#define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */
|
||||
#define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */
|
||||
#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
|
||||
#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
|
||||
#define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */
|
||||
#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
|
||||
#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 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_ARP 902 /* SMBus ARP Client */
|
||||
|
|
|
@ -43,6 +43,7 @@ struct snd_ak4xxx_ops {
|
|||
struct snd_akm4xxx_dac_channel {
|
||||
char *name; /* mixer volume name */
|
||||
unsigned int num_channels;
|
||||
char *switch_name; /* mixer switch*/
|
||||
};
|
||||
|
||||
/* ADC labels and channels */
|
||||
|
|
|
@ -1723,6 +1723,10 @@ struct snd_cs46xx {
|
|||
struct snd_cs46xx_pcm *playback_pcm;
|
||||
unsigned int play_ctl;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
u32 *saved_regs;
|
||||
#endif
|
||||
};
|
||||
|
||||
int snd_cs46xx_create(struct snd_card *card,
|
||||
|
|
|
@ -107,6 +107,7 @@ struct dsp_scb_descriptor {
|
|||
char scb_name[DSP_MAX_SCB_NAME];
|
||||
u32 address;
|
||||
int index;
|
||||
u32 *data;
|
||||
|
||||
struct dsp_scb_descriptor * sub_list_ptr;
|
||||
struct dsp_scb_descriptor * next_scb_ptr;
|
||||
|
@ -127,6 +128,7 @@ struct dsp_task_descriptor {
|
|||
int size;
|
||||
u32 address;
|
||||
int index;
|
||||
u32 *data;
|
||||
};
|
||||
|
||||
struct dsp_pcm_channel_descriptor {
|
||||
|
|
|
@ -1120,6 +1120,16 @@
|
|||
/************************************************************************************************/
|
||||
/* 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_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 */
|
||||
|
@ -1199,6 +1209,12 @@
|
|||
/************************************************************************************************/
|
||||
/* 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_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 */
|
||||
|
|
|
@ -38,6 +38,7 @@ enum sb_hw_type {
|
|||
SB_HW_ALS100, /* Avance Logic ALS100 chip */
|
||||
SB_HW_ALS4000, /* Avance Logic ALS4000 chip */
|
||||
SB_HW_DT019X, /* Diamond Tech. DT-019X / Avance Logic ALS-007 */
|
||||
SB_HW_CS5530, /* Cyrix/NatSemi 5530 VSA1 */
|
||||
};
|
||||
|
||||
#define SB_OPEN_PCM 0x01
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
/* include/version.h. Generated by alsa/ksync script. */
|
||||
#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)"
|
||||
|
|
|
@ -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
|
|
@ -65,6 +65,8 @@ source "sound/arm/Kconfig"
|
|||
|
||||
source "sound/mips/Kconfig"
|
||||
|
||||
source "sound/sh/Kconfig"
|
||||
|
||||
# the following will depend on the order of config.
|
||||
# here assuming USB is defined before ALSA
|
||||
source "sound/usb/Kconfig"
|
||||
|
|
|
@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
|
|||
obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
|
||||
obj-$(CONFIG_SOUND_PRIME) += 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/
|
||||
|
||||
# This one must be compilable even if sound is configured out
|
||||
|
|
|
@ -661,7 +661,7 @@ static struct transfer_info onyx_transfers[] = {
|
|||
.tag = 2,
|
||||
},
|
||||
#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 */
|
||||
.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) {
|
||||
/* mute and lock analog output */
|
||||
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
|
||||
if (onyx_write_register(onyx
|
||||
if (onyx_write_register(onyx,
|
||||
ONYX_REG_DAC_CONTROL,
|
||||
v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
|
||||
goto out_unlock;
|
||||
|
|
|
@ -1487,7 +1487,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
|
|||
|
||||
snd_pcm_stream_lock_irq(substream);
|
||||
/* resume pause */
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
|
||||
snd_pcm_pause(substream, 0);
|
||||
|
||||
/* pre-start/stop - all running streams are changed to DRAINING state */
|
||||
|
|
|
@ -109,7 +109,7 @@ void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
|
|||
spin_lock_irqsave(&list->lock, flags);
|
||||
while (instr->use) {
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
spin_lock_irqsave(&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;
|
||||
flist = instr->next;
|
||||
while (instr->use)
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
if (snd_seq_instr_free(instr, atomic)<0)
|
||||
snd_printk(KERN_WARNING "instrument free problem\n");
|
||||
instr = next;
|
||||
|
@ -555,7 +555,7 @@ static int instr_free(struct snd_seq_kinstr_ops *ops,
|
|||
SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
|
||||
while (instr->use) {
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
|
|
|
@ -1549,9 +1549,11 @@ static int snd_timer_user_info(struct file *file,
|
|||
int err = 0;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
t = tu->timeri->timer;
|
||||
snd_assert(t != NULL, return -ENXIO);
|
||||
if (!t)
|
||||
return -EBADFD;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (! info)
|
||||
|
@ -1579,9 +1581,11 @@ static int snd_timer_user_params(struct file *file,
|
|||
int err;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
t = tu->timeri->timer;
|
||||
snd_assert(t != NULL, return -ENXIO);
|
||||
if (!t)
|
||||
return -EBADFD;
|
||||
if (copy_from_user(¶ms, _params, sizeof(params)))
|
||||
return -EFAULT;
|
||||
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;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
memset(&status, 0, sizeof(status));
|
||||
status.tstamp = tu->tstamp;
|
||||
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;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
snd_timer_stop(tu->timeri);
|
||||
tu->timeri->lost = 0;
|
||||
tu->last_resolution = 0;
|
||||
|
@ -1708,7 +1714,8 @@ static int snd_timer_user_stop(struct file *file)
|
|||
struct snd_timer_user *tu;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1718,7 +1725,8 @@ static int snd_timer_user_continue(struct file *file)
|
|||
struct snd_timer_user *tu;
|
||||
|
||||
tu = file->private_data;
|
||||
snd_assert(tu->timeri != NULL, return -ENXIO);
|
||||
if (!tu->timeri)
|
||||
return -EBADFD;
|
||||
tu->timeri->lost = 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ static struct pnp_driver snd_mpu401_pnp_driver = {
|
|||
static struct pnp_driver snd_mpu401_pnp_driver;
|
||||
#endif
|
||||
|
||||
static void __init_or_module snd_mpu401_unregister_all(void)
|
||||
static void snd_mpu401_unregister_all(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
|
@ -833,7 +833,7 @@ static struct platform_driver snd_portman_driver = {
|
|||
/*********************************************************************
|
||||
* module init stuff
|
||||
*********************************************************************/
|
||||
static void __init_or_module snd_portman_unregister_all(void)
|
||||
static void snd_portman_unregister_all(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -481,8 +481,8 @@ static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
|
|||
int addr = AK_GET_ADDR(kcontrol->private_value);
|
||||
int shift = AK_GET_SHIFT(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)
|
||||
val = ! val;
|
||||
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;
|
||||
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));
|
||||
if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
|
||||
knew.name = "DAC Volume";
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# ALSA ISA drivers
|
||||
|
||||
menu "ISA devices"
|
||||
depends on SND!=n && ISA && ISA_DMA_API
|
||||
|
||||
config SND_AD1848_LIB
|
||||
tristate
|
||||
select SND_PCM
|
||||
|
@ -11,6 +8,22 @@ config SND_CS4231_LIB
|
|||
tristate
|
||||
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
|
||||
tristate "AdLib FM card"
|
||||
depends on SND
|
||||
|
@ -55,7 +68,7 @@ config SND_ALS100
|
|||
select ISAPNP
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB16_DSP
|
||||
help
|
||||
Say Y here to include support for soundcards based on Avance
|
||||
Logic ALS100, ALS110, ALS120 and ALS200 chips.
|
||||
|
@ -81,6 +94,7 @@ config SND_CMI8330
|
|||
tristate "C-Media CMI8330"
|
||||
depends on SND
|
||||
select SND_AD1848_LIB
|
||||
select SND_SB16_DSP
|
||||
help
|
||||
Say Y here to include support for soundcards based on the
|
||||
C-Media CMI8330 chip.
|
||||
|
@ -132,7 +146,7 @@ config SND_DT019X
|
|||
select ISAPNP
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB16_DSP
|
||||
help
|
||||
Say Y here to include support for soundcards based on the
|
||||
Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
|
||||
|
@ -145,7 +159,7 @@ config SND_ES968
|
|||
depends on SND && PNP && ISA
|
||||
select ISAPNP
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB8_DSP
|
||||
help
|
||||
Say Y here to include support for ESS AudioDrive ES968 chips.
|
||||
|
||||
|
@ -321,7 +335,7 @@ config SND_SB8
|
|||
depends on SND
|
||||
select SND_OPL3_LIB
|
||||
select SND_RAWMIDI
|
||||
select SND_PCM
|
||||
select SND_SB8_DSP
|
||||
help
|
||||
Say Y here to include support for Creative Sound Blaster 1.0/
|
||||
2.0/Pro (8-bit) or 100% compatible soundcards.
|
||||
|
@ -334,7 +348,7 @@ config SND_SB16
|
|||
depends on SND
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB16_DSP
|
||||
help
|
||||
Say Y here to include support for Sound Blaster 16 soundcards
|
||||
(including the Plug and Play version).
|
||||
|
@ -347,7 +361,7 @@ config SND_SBAWE
|
|||
depends on SND
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB16_DSP
|
||||
help
|
||||
Say Y here to include support for Sound Blaster AWE soundcards
|
||||
(including the Plug and Play version).
|
||||
|
|
|
@ -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");
|
||||
return;
|
||||
}
|
||||
time = schedule_timeout_interruptible(time);
|
||||
time = schedule_timeout(time);
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
}
|
||||
#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");
|
||||
return;
|
||||
}
|
||||
time = schedule_timeout_interruptible(time);
|
||||
time = schedule_timeout(time);
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
|
|
|
@ -164,6 +164,8 @@ static struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
|
|||
{ .id = "YMH0801", .devs = { { "YMH0021" } } },
|
||||
/* NeoMagic MagicWave 3DX */
|
||||
{ .id = "NMX2200", .devs = { { "YMH2210" } } },
|
||||
/* NeoMagic MagicWave 3D */
|
||||
{ .id = "NMX2200", .devs = { { "NMX2210" } } },
|
||||
/* --- */
|
||||
{ .id = "" } /* end */
|
||||
};
|
||||
|
|
|
@ -1927,10 +1927,12 @@ static struct snd_card *snd_opti9xx_card_new(void)
|
|||
static int __devinit snd_opti9xx_isa_match(struct device *devptr,
|
||||
unsigned int dev)
|
||||
{
|
||||
#ifdef CONFIG_PNP
|
||||
if (snd_opti9xx_pnp_is_probed)
|
||||
return 0;
|
||||
if (isapnp)
|
||||
return 0;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2096,6 +2098,7 @@ static int __init alsa_card_opti9xx_init(void)
|
|||
pnp_register_card_driver(&opti9xx_pnpc_driver);
|
||||
if (snd_opti9xx_pnp_is_probed)
|
||||
return 0;
|
||||
pnp_unregister_card_driver(&opti9xx_pnpc_driver);
|
||||
#endif
|
||||
return isa_register_driver(&snd_opti9xx_driver, 1);
|
||||
}
|
||||
|
|
|
@ -22,14 +22,13 @@ snd-es968-objs := es968.o
|
|||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o
|
||||
obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
|
||||
obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
|
||||
obj-$(CONFIG_SND_SB8) += snd-sb8.o
|
||||
obj-$(CONFIG_SND_SB16) += snd-sb16.o
|
||||
obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
|
||||
obj-$(CONFIG_SND_ES968) += snd-es968.o
|
||||
ifeq ($(CONFIG_SND_SB16_CSP),y)
|
||||
obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
|
||||
obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
|
||||
|
|
|
@ -563,6 +563,11 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
|
|||
__open_ok:
|
||||
if (chip->hardware == SB_HW_ALS100)
|
||||
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)
|
||||
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
|
||||
chip->playback_substream = substream;
|
||||
|
@ -633,6 +638,11 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
|
|||
__open_ok:
|
||||
if (chip->hardware == SB_HW_ALS100)
|
||||
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)
|
||||
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
|
||||
chip->capture_substream = substream;
|
||||
|
|
|
@ -128,7 +128,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
|
|||
minor = version & 0xff;
|
||||
snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
|
||||
chip->port, major, minor);
|
||||
|
||||
|
||||
switch (chip->hardware) {
|
||||
case SB_HW_AUTO:
|
||||
switch (major) {
|
||||
|
@ -168,6 +168,9 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
|
|||
case SB_HW_DT019X:
|
||||
str = "(DT019X/ALS007)";
|
||||
break;
|
||||
case SB_HW_CS5530:
|
||||
str = "16 (CS5530)";
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -821,6 +821,7 @@ int snd_sbmixer_new(struct snd_sb *chip)
|
|||
break;
|
||||
case SB_HW_16:
|
||||
case SB_HW_ALS100:
|
||||
case SB_HW_CS5530:
|
||||
if ((err = snd_sbmixer_init(chip,
|
||||
snd_sb16_controls,
|
||||
ARRAY_SIZE(snd_sb16_controls),
|
||||
|
@ -950,6 +951,7 @@ void snd_sbmixer_suspend(struct snd_sb *chip)
|
|||
break;
|
||||
case SB_HW_16:
|
||||
case SB_HW_ALS100:
|
||||
case SB_HW_CS5530:
|
||||
save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
|
||||
break;
|
||||
case SB_HW_ALS4000:
|
||||
|
@ -975,6 +977,7 @@ void snd_sbmixer_resume(struct snd_sb *chip)
|
|||
break;
|
||||
case SB_HW_16:
|
||||
case SB_HW_ALS100:
|
||||
case SB_HW_CS5530:
|
||||
restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
|
||||
break;
|
||||
case SB_HW_ALS4000:
|
||||
|
|
|
@ -382,7 +382,7 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
|
|||
unsigned long flags;
|
||||
unsigned char x;
|
||||
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
|
||||
spin_lock_irqsave(&s->lock, flags);
|
||||
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 char x;
|
||||
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
|
||||
spin_lock_irqsave(&s->lock, flags);
|
||||
x = inb(HOST_DATA_IO(s->io_base));
|
||||
|
|
|
@ -1780,7 +1780,7 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
|
|||
outb (val,port);
|
||||
spin_unlock_irq(&dev->irq_lock);
|
||||
while (1) {
|
||||
if ((timeout = schedule_timeout_interruptible(timeout)) == 0)
|
||||
if ((timeout = schedule_timeout(timeout)) == 0)
|
||||
return;
|
||||
if (dev->irq_ok)
|
||||
return;
|
||||
|
|
|
@ -33,6 +33,7 @@ config SND_ALS4000
|
|||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
select SND_SB_COMMON
|
||||
help
|
||||
Say Y here to include support for soundcards based on Avance Logic
|
||||
ALS4000 chips.
|
||||
|
@ -215,6 +216,16 @@ config SND_CS46XX_NEW_DSP
|
|||
|
||||
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
|
||||
tristate "CS5535/CS5536 Audio"
|
||||
depends on SND && X86 && !X86_64
|
||||
|
|
|
@ -12,6 +12,7 @@ snd-azt3328-objs := azt3328.o
|
|||
snd-bt87x-objs := bt87x.o
|
||||
snd-cmipci-objs := cmipci.o
|
||||
snd-cs4281-objs := cs4281.o
|
||||
snd-cs5530-objs := cs5530.o
|
||||
snd-ens1370-objs := ens1370.o
|
||||
snd-ens1371-objs := ens1371.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_CMIPCI) += snd-cmipci.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_ENS1371) += snd-ens1371.o
|
||||
obj-$(CONFIG_SND_ES1938) += snd-es1938.o
|
||||
|
|
|
@ -239,7 +239,7 @@ struct snd_ali_image {
|
|||
|
||||
|
||||
struct snd_ali {
|
||||
unsigned long irq;
|
||||
int irq;
|
||||
unsigned long port;
|
||||
unsigned char revision;
|
||||
|
||||
|
@ -731,8 +731,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
|
|||
return;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
while (count++ <= 50000) {
|
||||
for (count = 0; count <= 50000; count++) {
|
||||
snd_ali_delay(codec, 6);
|
||||
bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
|
||||
R2 = bval & 0x1F;
|
||||
|
@ -2343,7 +2342,7 @@ static int __devinit snd_ali_probe(struct pci_dev *pci,
|
|||
strcpy(card->driver, "ALI5451");
|
||||
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);
|
||||
|
||||
snd_ali_printk("register card.\n");
|
||||
|
|
|
@ -88,8 +88,8 @@
|
|||
#define PLAYBACK_BLOCK_COUNTER 0x9A
|
||||
#define RECORD_BLOCK_COUNTER 0x9B
|
||||
|
||||
#define DEBUG_CALLS 1
|
||||
#define DEBUG_PLAY_REC 1
|
||||
#define DEBUG_CALLS 0
|
||||
#define DEBUG_PLAY_REC 0
|
||||
|
||||
#if DEBUG_CALLS
|
||||
#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);
|
||||
|
||||
if (snd_als300_ac97(chip) < 0) {
|
||||
err = snd_als300_ac97(chip);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_WARNING "Could not create ac97\n");
|
||||
snd_als300_free(chip);
|
||||
return err;
|
||||
|
|
|
@ -168,6 +168,25 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
|
|||
#include "ca0106.h"
|
||||
|
||||
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] */
|
||||
{ .serial = 0x10021102,
|
||||
.name = "AudigyLS [SB0310]",
|
||||
|
|
|
@ -2897,6 +2897,10 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
kfree(chip->saved_regs);
|
||||
#endif
|
||||
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
|
@ -3140,6 +3144,23 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
@ -3214,19 +3235,7 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
|
|||
|
||||
snd_cs46xx_proc_start(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 */
|
||||
cs46xx_enable_stream_irqs(chip);
|
||||
|
||||
#ifndef CONFIG_SND_CS46XX_NEW_DSP
|
||||
/* set the attenuation to 0dB */
|
||||
|
@ -3665,11 +3674,19 @@ static struct cs_card_type __devinitdata cards[] = {
|
|||
* APM support
|
||||
*/
|
||||
#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)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct snd_cs46xx *chip = card->private_data;
|
||||
int amp_saved;
|
||||
int i, amp_saved;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
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_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;
|
||||
/* turn off amp */
|
||||
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_cs46xx *chip = card->private_data;
|
||||
int amp_saved;
|
||||
int i, amp_saved;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
|
@ -3716,6 +3737,16 @@ int snd_cs46xx_resume(struct pci_dev *pci)
|
|||
|
||||
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
|
||||
snd_cs46xx_codec_write(chip, BA0_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_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)
|
||||
chip->amplifier_ctrl(chip, 1); /* turn amp on */
|
||||
else
|
||||
|
@ -3896,6 +3934,15 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
|
|||
|
||||
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 */
|
||||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
|
|
|
@ -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);
|
||||
void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
|
||||
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,
|
||||
int symbol_type);
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
|
|
@ -306,13 +306,59 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
|
|||
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)
|
||||
{
|
||||
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 * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
|
||||
struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
|
||||
u32 doffset, dsize;
|
||||
int err;
|
||||
|
||||
if (ins->nmodules == DSP_MAX_MODULES - 1) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (parameter == NULL) {
|
||||
snd_printdd("dsp_spos: module got no parameter segment\n");
|
||||
} else {
|
||||
if (ins->nmodules > 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
err = dsp_load_parameter(chip, get_segment_desc(module,
|
||||
SEGTYPE_SP_PARAMETER));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ins->nmodules == 0) {
|
||||
snd_printdd("dsp_spos: clearing sample area\n");
|
||||
snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
|
||||
}
|
||||
|
||||
if (sample == NULL) {
|
||||
snd_printdd("dsp_spos: module got no sample segment\n");
|
||||
} else {
|
||||
if (ins->nmodules > 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
err = dsp_load_sample(chip, get_segment_desc(module,
|
||||
SEGTYPE_SP_SAMPLE));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ins->nmodules == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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].size = size;
|
||||
|
||||
|
@ -995,7 +1015,8 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
|
|||
desc = (ins->tasks + ins->ntask);
|
||||
ins->ntask++;
|
||||
|
||||
add_symbol (chip,name,dest,SYMBOL_PARAMETER);
|
||||
if (name)
|
||||
add_symbol (chip,name,dest,SYMBOL_PARAMETER);
|
||||
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);
|
||||
if (desc) {
|
||||
desc->data = scb_data;
|
||||
_dsp_create_scb(chip,scb_data,dest);
|
||||
} else {
|
||||
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);
|
||||
if (desc) {
|
||||
desc->data = task_data;
|
||||
_dsp_create_task_tree(chip,task_data,dest,size);
|
||||
} else {
|
||||
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
|
||||
};
|
||||
|
||||
/* dirty hack ... */
|
||||
_dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
|
||||
if (!cs46xx_dsp_create_task_tree(chip, NULL,
|
||||
(u32 *)&mix2_ostream_spb,
|
||||
WRITE_BACK_SPB, 2))
|
||||
goto _fail_end;
|
||||
}
|
||||
|
||||
/* input sample converter */
|
||||
|
@ -1622,7 +1647,6 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -51,9 +51,15 @@
|
|||
|
||||
#define HANA_FILENAME "emu/hana.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(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;
|
||||
}
|
||||
snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
|
||||
#if 0
|
||||
if (fw_entry->size != 0x133a4) {
|
||||
snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The FPGA is a Xilinx Spartan IIE XC2S50E */
|
||||
/* GPIO7 -> FPGA PGMN
|
||||
|
@ -694,6 +702,37 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
|
|||
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)
|
||||
{
|
||||
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) */
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
|
||||
snd_printdd("reg1=0x%x\n",reg);
|
||||
if (reg == 0x55) {
|
||||
if ((reg & 0x3f) == 0x15) {
|
||||
/* FPGA netlist already present so clear it */
|
||||
/* 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, ® );
|
||||
snd_printdd("reg2=0x%x\n",reg);
|
||||
if (reg == 0x55) {
|
||||
if ((reg & 0x3f) == 0x15) {
|
||||
/* FPGA failed to return to programming mode */
|
||||
snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
|
||||
if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
|
||||
snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
|
||||
return err;
|
||||
if (emu->card_capabilities->emu1010 == 1) {
|
||||
if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
|
||||
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. */
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
|
||||
if (reg != 0x55) {
|
||||
if ((reg & 0x3f) != 0x15) {
|
||||
/* FPGA failed to be programmed */
|
||||
snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
|
||||
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);
|
||||
snd_emu1010_fpga_link_dst_src_write(emu,
|
||||
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
|
||||
#if 0
|
||||
/* Original */
|
||||
|
@ -943,16 +1016,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
|
|||
/* Return to Audio Dock programming mode */
|
||||
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
|
||||
snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
|
||||
if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
|
||||
return err;
|
||||
if (emu->card_capabilities->emu1010 == 1) {
|
||||
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_read(emu, EMU_HANA_IRQ_STATUS, ® );
|
||||
snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
|
||||
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
|
||||
snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
|
||||
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 */
|
||||
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
|
||||
return 0;
|
||||
|
@ -1227,9 +1311,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
|
|||
.emu10k2_chip = 1,
|
||||
.ca0108_chip = 1,
|
||||
.ca_cardbus_chip = 1,
|
||||
.spi_dac = 1,
|
||||
.i2c_adc = 1,
|
||||
.spk71 = 1} ,
|
||||
.spk71 = 1 ,
|
||||
.emu1010 = 3} ,
|
||||
{.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,
|
||||
.driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
|
||||
.id = "Audigy2",
|
||||
|
@ -1663,12 +1753,13 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
|
|||
emu->fx8010.extout_mask = extout_mask;
|
||||
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 ((err = snd_emu10k1_ecard_init(emu)) < 0)
|
||||
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) {
|
||||
if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
|
||||
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)
|
||||
{
|
||||
if (emu->card_capabilities->ca_cardbus_chip)
|
||||
snd_emu10k1_cardbus_init(emu);
|
||||
if (emu->card_capabilities->ecard)
|
||||
snd_emu10k1_ecard_init(emu);
|
||||
else if (emu->card_capabilities->ca_cardbus_chip)
|
||||
snd_emu10k1_cardbus_init(emu);
|
||||
else if (emu->card_capabilities->emu1010)
|
||||
snd_emu10k1_emu1010_init(emu);
|
||||
else
|
||||
|
|
|
@ -1123,6 +1123,11 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
|
|||
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(
|
||||
struct snd_emu10k1_fx8010_code *icode,
|
||||
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);
|
||||
|
||||
#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+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);
|
||||
|
@ -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) {
|
||||
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); */
|
||||
/* 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) );
|
||||
/* Right ADC in 1 of 2 */
|
||||
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) );
|
||||
A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
|
||||
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;
|
||||
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);
|
||||
/* 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
|
||||
for (z = 4; z < 8; z++) {
|
||||
|
|
|
@ -77,6 +77,10 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Items labels in enum mixer controls assigning source data to
|
||||
* each destination
|
||||
*/
|
||||
static char *emu1010_src_texts[] = {
|
||||
"Silence",
|
||||
"Dock Mic A",
|
||||
|
@ -133,6 +137,9 @@ static char *emu1010_src_texts[] = {
|
|||
"DSP 31",
|
||||
};
|
||||
|
||||
/*
|
||||
* List of data sources available for each destination
|
||||
*/
|
||||
static unsigned int emu1010_src_regs[] = {
|
||||
EMU_SRC_SILENCE,/* 0 */
|
||||
EMU_SRC_DOCK_MIC_A1, /* 1 */
|
||||
|
@ -189,6 +196,10 @@ static unsigned int emu1010_src_regs[] = {
|
|||
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[] = {
|
||||
EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
|
||||
EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
|
||||
|
@ -216,6 +227,11 @@ static unsigned int emu1010_output_dst[] = {
|
|||
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[] = {
|
||||
EMU_DST_ALICE2_EMU32_0,
|
||||
EMU_DST_ALICE2_EMU32_1,
|
||||
|
|
|
@ -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;
|
||||
spin_lock_irq(&emu->reg_lock);
|
||||
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_RATE_44100 | SNDRV_PCM_RATE_48000 |
|
||||
* SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
|
||||
* SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
|
||||
* rate_min = 44100,
|
||||
* rate_max = 192000,
|
||||
* channels_min = 8,
|
||||
* channels_max = 8,
|
||||
* channels_min = 16,
|
||||
* channels_max = 16,
|
||||
* 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
|
||||
* 96kHz uses 2x channels over 48kHz
|
||||
* 192kHz uses 4x channels over 48kHz
|
||||
* So, for 48kHz 24bit, one has 8 channels
|
||||
* for 96kHz 24bit, one has 4 channels
|
||||
* for 192kHz 24bit, one has 2 channels
|
||||
* So, for 48kHz 24bit, one has 16 channels
|
||||
* for 96kHz 24bit, one has 8 channels
|
||||
* for 192kHz 24bit, one has 4 channels
|
||||
*
|
||||
*/
|
||||
#if 1
|
||||
switch (emu->emu1010.internal_clock) {
|
||||
|
@ -1258,13 +1260,15 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
|
|||
/* For 44.1kHz */
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_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;
|
||||
case 1:
|
||||
/* For 48kHz */
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_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;
|
||||
};
|
||||
#endif
|
||||
|
@ -1282,7 +1286,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
|
|||
#endif
|
||||
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
|
||||
/* 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 {
|
||||
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; */
|
||||
if (emu->audigy) {
|
||||
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 {
|
||||
emu->efx_voices_mask[0] = 0xffff0000;
|
||||
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);
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -1607,8 +1607,8 @@ struct es1371_quirk {
|
|||
unsigned char rev; /* revision */
|
||||
};
|
||||
|
||||
static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq,
|
||||
struct es1371_quirk *list)
|
||||
static int es1371_quirk_lookup(struct ensoniq *ensoniq,
|
||||
struct es1371_quirk *list)
|
||||
{
|
||||
while (list->vid != (unsigned short)PCI_ANY_ID) {
|
||||
if (ensoniq->pci->vendor == list->vid &&
|
||||
|
|
|
@ -341,6 +341,9 @@ struct azx {
|
|||
unsigned int single_cmd :1;
|
||||
unsigned int polling_mode :1;
|
||||
unsigned int msi :1;
|
||||
|
||||
/* for debugging */
|
||||
unsigned int last_cmd; /* last issued command (to sync) */
|
||||
};
|
||||
|
||||
/* driver types */
|
||||
|
@ -466,18 +469,10 @@ static void azx_free_cmd_io(struct azx *chip)
|
|||
}
|
||||
|
||||
/* send a command */
|
||||
static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
|
||||
unsigned int verb, unsigned int para)
|
||||
static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
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 */
|
||||
wp = azx_readb(chip, CORBWP);
|
||||
|
@ -538,12 +533,12 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
}
|
||||
if (! chip->rirb.cmds)
|
||||
return chip->rirb.res; /* the last value */
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
} while (time_after_eq(timeout, jiffies));
|
||||
|
||||
if (chip->msi) {
|
||||
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);
|
||||
chip->irq = -1;
|
||||
pci_disable_msi(chip->pci);
|
||||
|
@ -555,13 +550,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
|
||||
if (!chip->polling_mode) {
|
||||
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;
|
||||
goto again;
|
||||
}
|
||||
|
||||
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.cmds = 0;
|
||||
/* switch to single_cmd mode */
|
||||
|
@ -581,20 +578,11 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
*/
|
||||
|
||||
/* send a command */
|
||||
static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb,
|
||||
unsigned int para)
|
||||
static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
|
||||
{
|
||||
struct azx *chip = codec->bus->private_data;
|
||||
u32 val;
|
||||
int timeout = 50;
|
||||
|
||||
val = (u32)(codec->addr & 0x0f) << 28;
|
||||
val |= (u32)direct << 27;
|
||||
val |= (u32)nid << 20;
|
||||
val |= verb << 8;
|
||||
val |= para;
|
||||
|
||||
while (timeout--) {
|
||||
/* check ICB busy bit */
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return azx_single_send_cmd(codec, nid, direct, verb, para);
|
||||
return azx_single_send_cmd(codec, val);
|
||||
else
|
||||
return azx_corb_send_cmd(codec, nid, direct, verb, para);
|
||||
return azx_corb_send_cmd(codec, val);
|
||||
}
|
||||
|
||||
/* 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, 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, 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, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
|
|
@ -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, "Subsystem Id: 0x%x\n", codec->subsystem_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)
|
||||
return;
|
||||
snd_iprintf(buffer, "Default PCM:\n");
|
||||
|
|
|
@ -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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -61,7 +62,7 @@ struct ad198x_spec {
|
|||
int num_channel_mode;
|
||||
|
||||
/* 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 */
|
||||
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
|
||||
*/
|
||||
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 = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
|
||||
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
|
||||
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
|
||||
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
|
||||
|
|
|
@ -172,6 +172,7 @@ static int patch_atihdmi(struct hda_codec *codec)
|
|||
*/
|
||||
struct hda_codec_preset snd_hda_preset_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 = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
|
||||
{} /* terminator */
|
||||
|
|
|
@ -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, 0x30b5, "HP DV2120", 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, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
|
||||
SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
|
||||
{}
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -304,8 +304,12 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
|
|||
{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
|
||||
{ .id = 0x10573057, .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) */
|
||||
{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
|
||||
/* LG LW20 modem */
|
||||
{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ enum {
|
|||
|
||||
enum {
|
||||
STAC_9205_REF,
|
||||
STAC_M43xx,
|
||||
STAC_9205_MODELS
|
||||
};
|
||||
|
||||
|
@ -59,11 +60,19 @@ enum {
|
|||
STAC_D945_REF,
|
||||
STAC_D945GTP3,
|
||||
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_MACBOOK,
|
||||
STAC_MACBOOK_PRO_V1,
|
||||
STAC_MACBOOK_PRO_V2,
|
||||
STAC_IMAC_INTEL,
|
||||
STAC_IMAC_INTEL_20,
|
||||
STAC_922X_MODELS
|
||||
};
|
||||
|
||||
|
@ -210,7 +219,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
|
|||
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x14, 0x16, 0x17, 0x18,
|
||||
0x21, 0x22,
|
||||
|
||||
};
|
||||
|
||||
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[] = {
|
||||
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,
|
||||
.name = "Input Source",
|
||||
|
@ -549,44 +555,78 @@ static unsigned int d945gtp5_pin_configs[10] = {
|
|||
0x02a19320, 0x40000100,
|
||||
};
|
||||
|
||||
static unsigned int macbook_pro_v1_pin_configs[10] = {
|
||||
0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010,
|
||||
0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e,
|
||||
0x02a19320, 0x400000fb
|
||||
};
|
||||
|
||||
static unsigned int macbook_pro_v2_pin_configs[10] = {
|
||||
0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
|
||||
0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
|
||||
static unsigned int intel_mac_v1_pin_configs[10] = {
|
||||
0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
|
||||
0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
|
||||
0x400000fc, 0x400000fb,
|
||||
};
|
||||
|
||||
static unsigned int imac_intel_pin_configs[10] = {
|
||||
0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe,
|
||||
0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa,
|
||||
static unsigned int intel_mac_v2_pin_configs[10] = {
|
||||
0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
|
||||
0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
|
||||
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] = {
|
||||
[STAC_D945_REF] = ref922x_pin_configs,
|
||||
[STAC_D945GTP3] = d945gtp3_pin_configs,
|
||||
[STAC_D945GTP5] = d945gtp5_pin_configs,
|
||||
[STAC_MACMINI] = macbook_pro_v1_pin_configs,
|
||||
[STAC_MACBOOK] = macbook_pro_v1_pin_configs,
|
||||
[STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs,
|
||||
[STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs,
|
||||
[STAC_IMAC_INTEL] = imac_intel_pin_configs,
|
||||
[STAC_922X_DELL] = stac922x_dell_pin_configs,
|
||||
[STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
|
||||
[STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
|
||||
[STAC_INTEL_MAC_V3] = intel_mac_v3_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] = {
|
||||
[STAC_D945_REF] = "ref",
|
||||
[STAC_D945GTP5] = "5stack",
|
||||
[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_MACBOOK] = "macbook",
|
||||
[STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
|
||||
[STAC_MACBOOK_PRO_V2] = "macbook-pro",
|
||||
[STAC_IMAC_INTEL] = "imac-intel",
|
||||
[STAC_IMAC_INTEL_20] = "imac-intel-20",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac922x_cfg_tbl[] = {
|
||||
|
@ -649,7 +689,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
|
|||
/* other systems */
|
||||
/* Apple Mac Mini (early 2006) */
|
||||
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 */
|
||||
};
|
||||
|
||||
|
@ -730,7 +773,8 @@ static unsigned int ref9205_pin_configs[12] = {
|
|||
};
|
||||
|
||||
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] = {
|
||||
|
@ -741,6 +785,10 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
|
|||
/* SigmaTel reference board */
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
|
||||
"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 */
|
||||
};
|
||||
|
||||
|
@ -770,33 +818,56 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
|
|||
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)
|
||||
{
|
||||
int i;
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
unsigned int pin_cfg;
|
||||
|
||||
if (! spec->pin_nids || ! spec->pin_configs)
|
||||
return;
|
||||
if (!spec->pin_configs)
|
||||
return;
|
||||
|
||||
for (i = 0; i < spec->num_pins; i++) {
|
||||
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
|
||||
AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
|
||||
spec->pin_configs[i] & 0x000000ff);
|
||||
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
|
||||
AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
|
||||
(spec->pin_configs[i] & 0x0000ff00) >> 8);
|
||||
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
|
||||
AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
|
||||
(spec->pin_configs[i] & 0x00ff0000) >> 16);
|
||||
snd_hda_codec_write(codec, spec->pin_nids[i], 0,
|
||||
AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
|
||||
spec->pin_configs[i] >> 24);
|
||||
pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
|
||||
AC_VERB_GET_CONFIG_DEFAULT,
|
||||
0x00);
|
||||
snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
|
||||
}
|
||||
for (i = 0; i < spec->num_pins; i++)
|
||||
stac92xx_set_config_reg(codec, spec->pin_nids[i],
|
||||
spec->pin_configs[i]);
|
||||
}
|
||||
|
||||
static void stac92xx_enable_gpio_mask(struct hda_codec *codec,
|
||||
int gpio_mask, int gpio_data)
|
||||
{
|
||||
/* Configure GPIOx as output */
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, gpio_mask);
|
||||
/* Configure GPIOx as CMOS */
|
||||
snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
|
||||
/* Assert GPIOx */
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_DATA, gpio_data);
|
||||
/* 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.
|
||||
*/
|
||||
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;
|
||||
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 (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 */
|
||||
snd_printk(KERN_ERR
|
||||
"%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;
|
||||
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++) {
|
||||
static const char *pfxs[] = {
|
||||
"Speaker", "External Speaker", "Speaker2",
|
||||
|
@ -1891,7 +1977,7 @@ static int patch_stac9200(struct hda_codec *codec)
|
|||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->num_pins = 8;
|
||||
spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
|
||||
spec->pin_nids = stac9200_pin_nids;
|
||||
spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
|
||||
stac9200_models,
|
||||
|
@ -1941,7 +2027,7 @@ static int patch_stac925x(struct hda_codec *codec)
|
|||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->num_pins = 8;
|
||||
spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
|
||||
spec->pin_nids = stac925x_pin_nids;
|
||||
spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
|
||||
stac925x_models,
|
||||
|
@ -2013,29 +2099,41 @@ static int patch_stac922x(struct hda_codec *codec)
|
|||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->num_pins = 10;
|
||||
spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
|
||||
spec->pin_nids = stac922x_pin_nids;
|
||||
spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
|
||||
stac922x_models,
|
||||
stac922x_cfg_tbl);
|
||||
if (spec->board_config == STAC_MACMINI) {
|
||||
if (spec->board_config == STAC_INTEL_MAC_V3) {
|
||||
spec->gpio_mute = 1;
|
||||
/* Intel Macs have all same PCI SSID, so we need to check
|
||||
* codec SSID to distinguish the exact models
|
||||
*/
|
||||
printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", 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;
|
||||
case 0x106b0200: /* MacBook Pro first generation */
|
||||
spec->board_config = STAC_MACBOOK_PRO_V1;
|
||||
case 0x106b0600:
|
||||
case 0x106b0700:
|
||||
spec->board_config = STAC_INTEL_MAC_V2;
|
||||
break;
|
||||
case 0x106b1e00: /* MacBook Pro second generation */
|
||||
spec->board_config = STAC_MACBOOK_PRO_V2;
|
||||
case 0x106b0e00:
|
||||
case 0x106b0f00:
|
||||
case 0x106b1600:
|
||||
case 0x106b1700:
|
||||
case 0x106b0200:
|
||||
case 0x106b1e00:
|
||||
spec->board_config = STAC_INTEL_MAC_V3;
|
||||
break;
|
||||
case 0x106b0700: /* Intel-based iMac */
|
||||
spec->board_config = STAC_IMAC_INTEL;
|
||||
case 0x106b1a00:
|
||||
case 0x00000100:
|
||||
spec->board_config = STAC_INTEL_MAC_V4;
|
||||
break;
|
||||
case 0x106b0a00:
|
||||
case 0x106b2200:
|
||||
spec->board_config = STAC_INTEL_MAC_V5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2082,6 +2180,13 @@ static int patch_stac922x(struct hda_codec *codec)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2095,7 +2200,7 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->num_pins = 14;
|
||||
spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
|
||||
spec->pin_nids = stac927x_pin_nids;
|
||||
spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
|
||||
stac927x_models,
|
||||
|
@ -2141,7 +2246,9 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
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);
|
||||
if (!err) {
|
||||
if (spec->board_config < 0) {
|
||||
|
@ -2159,27 +2266,20 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
static int patch_stac9205(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec;
|
||||
int err;
|
||||
int err, gpio_mask, gpio_data;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
spec->num_pins = 14;
|
||||
spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
|
||||
spec->pin_nids = stac9205_pin_nids;
|
||||
spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
|
||||
stac9205_models,
|
||||
|
@ -2209,19 +2309,21 @@ static int patch_stac9205(struct hda_codec *codec)
|
|||
spec->mixer = stac9205_mixer;
|
||||
|
||||
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 */
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
|
||||
/* Configure GPIO0 as CMOS */
|
||||
snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
|
||||
/* Assert GPIO0 high */
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_DATA, 0x00000001);
|
||||
/* Enable GPIO0 */
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_MASK, 0x00000001);
|
||||
gpio_mask = 0x00000007; /* GPIO0-2 */
|
||||
/* GPIO0 High = EAPD, GPIO1 Low = DRM,
|
||||
* GPIO2 High = Headphone Mute
|
||||
*/
|
||||
gpio_data = 0x00000005;
|
||||
} else
|
||||
gpio_mask = gpio_data = 0x00000001; /* GPIO0 High = EAPD */
|
||||
|
||||
stac92xx_enable_gpio_mask(codec, gpio_mask, gpio_data);
|
||||
err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
|
||||
if (!err) {
|
||||
if (spec->board_config < 0) {
|
||||
|
@ -2256,8 +2358,8 @@ static struct hda_input_mux vaio_mux = {
|
|||
.num_items = 2,
|
||||
.items = {
|
||||
/* { "HP", 0x0 }, */
|
||||
{ "Line", 0x1 },
|
||||
{ "Mic", 0x2 },
|
||||
{ "Mic Jack", 0x1 },
|
||||
{ "Internal Mic", 0x2 },
|
||||
{ "PCM", 0x3 },
|
||||
}
|
||||
};
|
||||
|
@ -2268,7 +2370,7 @@ static struct hda_verb vaio_init[] = {
|
|||
{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
|
||||
{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
|
||||
{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 */
|
||||
{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 */
|
||||
|
@ -2284,7 +2386,7 @@ static struct hda_verb vaio_ar_init[] = {
|
|||
{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
|
||||
/* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
|
||||
{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 */
|
||||
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
|
||||
/* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
|
||||
|
|
|
@ -186,7 +186,12 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
|
|||
#define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
|
||||
|
||||
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[] = {
|
||||
|
|
|
@ -1533,7 +1533,8 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
|
|||
printk(KERN_ERR " force the driver to load by "
|
||||
"passing in the module parameter\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;
|
||||
goto __error;
|
||||
}
|
||||
|
|
|
@ -406,7 +406,7 @@ static snd_pcm_uframes_t rme9652_hw_pointer(struct snd_rme9652 *rme9652)
|
|||
} else if (!frag)
|
||||
return 0;
|
||||
offset -= rme9652->max_jitter;
|
||||
if (offset < 0)
|
||||
if ((int)offset < 0)
|
||||
offset += period_size * 2;
|
||||
} else {
|
||||
if (offset > period_size + rme9652->max_jitter) {
|
||||
|
|
|
@ -2098,7 +2098,7 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
|
|||
pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
|
||||
if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
|
||||
break;
|
||||
schedule_timeout_uninterruptible(1);
|
||||
schedule_timeout(1);
|
||||
} while (time_before(jiffies, end_time));
|
||||
|
||||
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;
|
||||
goto __ac97_ok2;
|
||||
}
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
} while (time_before(jiffies, end_time));
|
||||
/* This is ok, the most of motherboards have only one codec */
|
||||
|
||||
|
|
|
@ -983,7 +983,7 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
|
|||
pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
|
||||
if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
|
||||
break;
|
||||
schedule_timeout_uninterruptible(1);
|
||||
schedule_timeout(1);
|
||||
} while (time_before(jiffies, end_time));
|
||||
|
||||
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;
|
||||
goto __ac97_ok2;
|
||||
}
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout(1);
|
||||
} while (time_before(jiffies, end_time));
|
||||
/* This is ok, the most of motherboards have only one codec */
|
||||
|
||||
|
|
|
@ -33,3 +33,23 @@ config SND_POWERMAC_AUTO_DRC
|
|||
option.
|
||||
|
||||
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
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
|
||||
obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
|
||||
obj-$(CONFIG_SND_PS3) += snd_ps3.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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_ */
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for ALSA
|
||||
#
|
||||
|
||||
snd-aica-objs := aica.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_AICA) += snd-aica.o
|
|
@ -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);
|
|
@ -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;
|
||||
};
|
|
@ -27,6 +27,7 @@ config SND_SOC
|
|||
source "sound/soc/at91/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/s3c24xx/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
|
||||
# Supported codecs
|
||||
source "sound/soc/codecs/Kconfig"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
snd-soc-core-objs := soc-core.o soc-dapm.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/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
config SND_S3C24XX_SOC
|
||||
tristate "SoC Audio for the Samsung S3C24XX chips"
|
||||
depends on ARCH_S3C2410 && SND_SOC
|
||||
select SND_PCM
|
||||
help
|
||||
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
|
||||
|
@ -8,3 +9,29 @@ config SND_S3C24XX_SOC
|
|||
|
||||
config SND_S3C24XX_SOC_I2S
|
||||
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.
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
# S3c24XX Platform Support
|
||||
snd-soc-s3c24xx-objs := s3c24xx-pcm.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_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
|
||||
|
|
|
@ -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_*/
|
||||
|
|
@ -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");
|
|
@ -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");
|
|
@ -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_*/
|
|
@ -344,11 +344,11 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
|
|||
DBG("Entered %s\n", __FUNCTION__);
|
||||
|
||||
switch (div_id) {
|
||||
case S3C24XX_DIV_MCLK:
|
||||
case S3C24XX_DIV_BCLK:
|
||||
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
|
||||
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
break;
|
||||
case S3C24XX_DIV_BCLK:
|
||||
case S3C24XX_DIV_MCLK:
|
||||
reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
|
||||
writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
|
||||
break;
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -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>");
|
|
@ -2350,7 +2350,9 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
|
|||
return 1;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
* 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 {
|
||||
pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
|
||||
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,
|
||||
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_DTS)
|
||||
&& altno != 6)
|
||||
|
|
|
@ -52,6 +52,24 @@
|
|||
.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 = 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 |
|
||||
USB_DEVICE_ID_MATCH_INT_CLASS |
|
||||
|
@ -1051,7 +1069,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
|||
.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 */
|
||||
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.
|
||||
* 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 */
|
||||
{
|
||||
/* 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 */
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) {
|
||||
kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
|
||||
usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
|
||||
}
|
||||
kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
|
||||
usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
|
||||
|
||||
kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
|
||||
usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue