Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (290 commits) ALSA: pcm - Update document about xrun_debug proc file ALSA: lx6464es - support standard alsa module parameters ALSA: snd_usb_caiaq: set mixername ALSA: hda - add quirk for STAC92xx (SigmaTel STAC9205) ALSA: use card device as parent for jack input-devices ALSA: sound/ps3: Correct existing and add missing annotations ALSA: sound/ps3: Restructure driver source ALSA: sound/ps3: Fix checkpatch issues ASoC: Fix lm4857 control ALSA: ctxfi - Clear PCM resources at hw_params and hw_free ALSA: ctxfi - Check the presence of SRC instance in PCM pointer callbacks ALSA: ctxfi - Add missing start check in atc_pcm_playback_start() ALSA: ctxfi - Add use_system_timer module option ALSA: usb - Add boot quirk for C-Media 6206 USB Audio ALSA: ctxfi - Fix wrong model id for UAA ALSA: ctxfi - Clean up probe routines ALSA: hda - Fix the previous tagra-8ch patch ALSA: hda - Add 7.1 support for MSI GX620 ALSA: pcm - A helper function to compose PCM stream name for debug prints ALSA: emu10k1 - Fix minimum periods for efx playback ...
This commit is contained in:
commit
e349792a38
213 changed files with 33620 additions and 4038 deletions
|
@ -460,6 +460,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
The power-management is supported.
|
||||
|
||||
Module snd-ctxfi
|
||||
----------------
|
||||
|
||||
Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
|
||||
* Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
|
||||
* Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
|
||||
* Creative Sound Blaster X-Fi Titanium Professional Audio
|
||||
* Creative Sound Blaster X-Fi Titanium
|
||||
* Creative Sound Blaster X-Fi Elite Pro
|
||||
* Creative Sound Blaster X-Fi Platinum
|
||||
* Creative Sound Blaster X-Fi Fatal1ty
|
||||
* Creative Sound Blaster X-Fi XtremeGamer
|
||||
* Creative Sound Blaster X-Fi XtremeMusic
|
||||
|
||||
reference_rate - reference sample rate, 44100 or 48000 (default)
|
||||
multiple - multiple to ref. sample rate, 1 or 2 (default)
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
Module snd-darla20
|
||||
------------------
|
||||
|
||||
|
@ -925,6 +944,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
* Onkyo SE-90PCI
|
||||
* Onkyo SE-200PCI
|
||||
* ESI Juli@
|
||||
* ESI Maya44
|
||||
* Hercules Fortissimo IV
|
||||
* EGO-SYS WaveTerminal 192M
|
||||
|
||||
|
@ -933,7 +953,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
prodigy71xt, prodigy71hifi, prodigyhd2, prodigy192,
|
||||
juli, aureon51, aureon71, universe, ap192, k8x800,
|
||||
phase22, phase28, ms300, av710, se200pci, se90pci,
|
||||
fortissimo4, sn25p, WT192M
|
||||
fortissimo4, sn25p, WT192M, maya44
|
||||
|
||||
This module supports multiple cards and autoprobe.
|
||||
|
||||
|
@ -1093,6 +1113,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
This module supports multiple cards.
|
||||
The driver requires the firmware loader support on kernel.
|
||||
|
||||
Module snd-lx6464es
|
||||
-------------------
|
||||
|
||||
Module for Digigram LX6464ES boards
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
Module snd-maestro3
|
||||
-------------------
|
||||
|
||||
|
@ -1543,13 +1570,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
Module snd-sc6000
|
||||
-----------------
|
||||
|
||||
Module for Gallant SC-6000 soundcard.
|
||||
Module for Gallant SC-6000 soundcard and later models: SC-6600
|
||||
and SC-7000.
|
||||
|
||||
port - Port # (0x220 or 0x240)
|
||||
mss_port - MSS Port # (0x530 or 0xe80)
|
||||
irq - IRQ # (5,7,9,10,11)
|
||||
mpu_irq - MPU-401 IRQ # (5,7,9,10) ,0 - no MPU-401 irq
|
||||
dma - DMA # (1,3,0)
|
||||
joystick - Enable gameport - 0 = disable (default), 1 = enable
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
|
@ -1859,7 +1888,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
-------------------
|
||||
|
||||
Module for sound cards based on the Asus AV100/AV200 chips,
|
||||
i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), and Essence STX.
|
||||
i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), Essence ST
|
||||
(Deluxe) and Essence STX.
|
||||
|
||||
This module supports autoprobe and multiple cards.
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ ALC260
|
|||
acer Acer TravelMate
|
||||
will Will laptops (PB V7900)
|
||||
replacer Replacer 672V
|
||||
favorit100 Maxdata Favorit 100XS
|
||||
basic fixed pin assignment (old default model)
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
|
@ -85,10 +86,11 @@ ALC269
|
|||
eeepc-p703 ASUS Eeepc P703 P900A
|
||||
eeepc-p901 ASUS Eeepc P901 S101
|
||||
fujitsu FSC Amilo
|
||||
lifebook Fujitsu Lifebook S6420
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC662/663
|
||||
==========
|
||||
ALC662/663/272
|
||||
==============
|
||||
3stack-dig 3-stack (2-channel) with SPDIF
|
||||
3stack-6ch 3-stack (6-channel)
|
||||
3stack-6ch-dig 3-stack (6-channel) with SPDIF
|
||||
|
@ -107,6 +109,9 @@ ALC662/663
|
|||
asus-mode4 ASUS
|
||||
asus-mode5 ASUS
|
||||
asus-mode6 ASUS
|
||||
dell Dell with ALC272
|
||||
dell-zm1 Dell ZM1 with ALC272
|
||||
samsung-nc10 Samsung NC10 mini notebook
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC882/885
|
||||
|
@ -118,6 +123,7 @@ ALC882/885
|
|||
asus-a7j ASUS A7J
|
||||
asus-a7m ASUS A7M
|
||||
macpro MacPro support
|
||||
mb5 Macbook 5,1
|
||||
mbp3 Macbook Pro rev3
|
||||
imac24 iMac 24'' with jack detection
|
||||
w2jc ASUS W2JC
|
||||
|
@ -133,10 +139,12 @@ ALC883/888
|
|||
acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
|
||||
acer-aspire Acer Aspire 9810
|
||||
acer-aspire-4930g Acer Aspire 4930G
|
||||
acer-aspire-8930g Acer Aspire 8930G
|
||||
medion Medion Laptops
|
||||
medion-md2 Medion MD2
|
||||
targa-dig Targa/MSI
|
||||
targa-2ch-dig Targs/MSI with 2-channel
|
||||
targa-2ch-dig Targa/MSI with 2-channel
|
||||
targa-8ch-dig Targa/MSI with 8-channel (MSI GX620)
|
||||
laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
|
||||
lenovo-101e Lenovo 101E
|
||||
lenovo-nb0763 Lenovo NB0763
|
||||
|
@ -150,6 +158,9 @@ ALC883/888
|
|||
fujitsu-pi2515 Fujitsu AMILO Pi2515
|
||||
fujitsu-xa3530 Fujitsu AMILO XA3530
|
||||
3stack-6ch-intel Intel DG33* boards
|
||||
asus-p5q ASUS P5Q-EM boards
|
||||
mb31 MacBook 3,1
|
||||
sony-vaio-tt Sony VAIO TT
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861/660
|
||||
|
@ -348,6 +359,7 @@ STAC92HD71B*
|
|||
hp-m4 HP mini 1000
|
||||
hp-dv5 HP dv series
|
||||
hp-hdx HP HDX series
|
||||
hp-dv4-1222nr HP dv4-1222nr (with LED support)
|
||||
auto BIOS setup (default)
|
||||
|
||||
STAC92HD73*
|
||||
|
|
|
@ -88,26 +88,34 @@ card*/pcm*/info
|
|||
substreams, etc.
|
||||
|
||||
card*/pcm*/xrun_debug
|
||||
This file appears when CONFIG_SND_DEBUG=y.
|
||||
This shows the status of xrun (= buffer overrun/xrun) debug of
|
||||
ALSA PCM middle layer, as an integer from 0 to 2. The value
|
||||
can be changed by writing to this file, such as
|
||||
This file appears when CONFIG_SND_DEBUG=y and
|
||||
CONFIG_PCM_XRUN_DEBUG=y.
|
||||
This shows the status of xrun (= buffer overrun/xrun) and
|
||||
invalid PCM position debug/check of ALSA PCM middle layer.
|
||||
It takes an integer value, can be changed by writing to this
|
||||
file, such as
|
||||
|
||||
# cat 2 > /proc/asound/card0/pcm0p/xrun_debug
|
||||
# cat 5 > /proc/asound/card0/pcm0p/xrun_debug
|
||||
|
||||
When this value is greater than 0, the driver will show the
|
||||
messages to kernel log when an xrun is detected. The debug
|
||||
message is shown also when the invalid H/W pointer is detected
|
||||
at the update of periods (usually called from the interrupt
|
||||
The value consists of the following bit flags:
|
||||
bit 0 = Enable XRUN/jiffies debug messages
|
||||
bit 1 = Show stack trace at XRUN / jiffies check
|
||||
bit 2 = Enable additional jiffies check
|
||||
|
||||
When the bit 0 is set, the driver will show the messages to
|
||||
kernel log when an xrun is detected. The debug message is
|
||||
shown also when the invalid H/W pointer is detected at the
|
||||
update of periods (usually called from the interrupt
|
||||
handler).
|
||||
|
||||
When this value is greater than 1, the driver will show the
|
||||
stack trace additionally. This may help the debugging.
|
||||
When the bit 1 is set, the driver will show the stack trace
|
||||
additionally. This may help the debugging.
|
||||
|
||||
Since 2.6.30, this option also enables the hwptr check using
|
||||
Since 2.6.30, this option can enable the hwptr check using
|
||||
jiffies. This detects spontaneous invalid pointer callback
|
||||
values, but can be lead to too much corrections for a (mostly
|
||||
buggy) hardware that doesn't give smooth pointer updates.
|
||||
This feature is enabled via the bit 2.
|
||||
|
||||
card*/pcm*/sub*/info
|
||||
The general information of this PCM sub-stream.
|
||||
|
|
163
Documentation/sound/alsa/README.maya44
Normal file
163
Documentation/sound/alsa/README.maya44
Normal file
|
@ -0,0 +1,163 @@
|
|||
NOTE: The following is the original document of Rainer's patch that the
|
||||
current maya44 code based on. Some contents might be obsoleted, but I
|
||||
keep here as reference -- tiwai
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
STATE OF DEVELOPMENT:
|
||||
|
||||
This driver is being developed on the initiative of Piotr Makowski (oponek@gmail.com) and financed by Lars Bergmann.
|
||||
Development is carried out by Rainer Zimmermann (mail@lightshed.de).
|
||||
|
||||
ESI provided a sample Maya44 card for the development work.
|
||||
|
||||
However, unfortunately it has turned out difficult to get detailed programming information, so I (Rainer Zimmermann) had to find out some card-specific information by experiment and conjecture. Some information (in particular, several GPIO bits) is still missing.
|
||||
|
||||
This is the first testing version of the Maya44 driver released to the alsa-devel mailing list (Feb 5, 2008).
|
||||
|
||||
|
||||
The following functions work, as tested by Rainer Zimmermann and Piotr Makowski:
|
||||
|
||||
- playback and capture at all sampling rates
|
||||
- input/output level
|
||||
- crossmixing
|
||||
- line/mic switch
|
||||
- phantom power switch
|
||||
- analogue monitor a.k.a bypass
|
||||
|
||||
|
||||
The following functions *should* work, but are not fully tested:
|
||||
|
||||
- Channel 3+4 analogue - S/PDIF input switching
|
||||
- S/PDIF output
|
||||
- all inputs/outputs on the M/IO/DIO extension card
|
||||
- internal/external clock selection
|
||||
|
||||
|
||||
*In particular, we would appreciate testing of these functions by anyone who has access to an M/IO/DIO extension card.*
|
||||
|
||||
|
||||
Things that do not seem to work:
|
||||
|
||||
- The level meters ("multi track") in 'alsamixer' do not seem to react to signals in (if this is a bug, it would probably be in the existing ICE1724 code).
|
||||
|
||||
- Ardour 2.1 seems to work only via JACK, not using ALSA directly or via OSS. This still needs to be tracked down.
|
||||
|
||||
|
||||
DRIVER DETAILS:
|
||||
|
||||
the following files were added:
|
||||
|
||||
pci/ice1724/maya44.c - Maya44 specific code
|
||||
pci/ice1724/maya44.h
|
||||
pci/ice1724/ice1724.patch
|
||||
pci/ice1724/ice1724.h.patch - PROPOSED patch to ice1724.h (see SAMPLING RATES)
|
||||
i2c/other/wm8776.c - low-level access routines for Wolfson WM8776 codecs
|
||||
include/wm8776.h
|
||||
|
||||
|
||||
Note that the wm8776.c code is meant to be card-independent and does not actually register the codec with the ALSA infrastructure.
|
||||
This is done in maya44.c, mainly because some of the WM8776 controls are used in Maya44-specific ways, and should be named appropriately.
|
||||
|
||||
|
||||
the following files were created in pci/ice1724, simply #including the corresponding file from the alsa-kernel tree:
|
||||
|
||||
wtm.h
|
||||
vt1720_mobo.h
|
||||
revo.h
|
||||
prodigy192.h
|
||||
pontis.h
|
||||
phase.h
|
||||
maya44.h
|
||||
juli.h
|
||||
aureon.h
|
||||
amp.h
|
||||
envy24ht.h
|
||||
se.h
|
||||
prodigy_hifi.h
|
||||
|
||||
|
||||
*I hope this is the correct way to do things.*
|
||||
|
||||
|
||||
SAMPLING RATES:
|
||||
|
||||
The Maya44 card (or more exactly, the Wolfson WM8776 codecs) allow a maximum sampling rate of 192 kHz for playback and 92 kHz for capture.
|
||||
|
||||
As the ICE1724 chip only allows one global sampling rate, this is handled as follows:
|
||||
|
||||
* setting the sampling rate on any open PCM device on the maya44 card will always set the *global* sampling rate for all playback and capture channels.
|
||||
|
||||
* In the current state of the driver, setting rates of up to 192 kHz is permitted even for capture devices.
|
||||
|
||||
*AVOID CAPTURING AT RATES ABOVE 96kHz*, even though it may appear to work. The codec cannot actually capture at such rates, meaning poor quality.
|
||||
|
||||
|
||||
I propose some additional code for limiting the sampling rate when setting on a capture pcm device. However because of the global sampling rate, this logic would be somewhat problematic.
|
||||
|
||||
The proposed code (currently deactivated) is in ice1712.h.patch, ice1724.c and maya44.c (in pci/ice1712).
|
||||
|
||||
|
||||
SOUND DEVICES:
|
||||
|
||||
PCM devices correspond to inputs/outputs as follows (assuming Maya44 is card #0):
|
||||
|
||||
hw:0,0 input - stereo, analog input 1+2
|
||||
hw:0,0 output - stereo, analog output 1+2
|
||||
hw:0,1 input - stereo, analog input 3+4 OR S/PDIF input
|
||||
hw:0,1 output - stereo, analog output 3+4 (and SPDIF out)
|
||||
|
||||
|
||||
NAMING OF MIXER CONTROLS:
|
||||
|
||||
(for more information about the signal flow, please refer to the block diagram on p.24 of the ESI Maya44 manual, or in the ESI windows software).
|
||||
|
||||
|
||||
PCM: (digital) output level for channel 1+2
|
||||
PCM 1: same for channel 3+4
|
||||
|
||||
Mic Phantom+48V: switch for +48V phantom power for electrostatic microphones on input 1/2.
|
||||
Make sure this is not turned on while any other source is connected to input 1/2.
|
||||
It might damage the source and/or the maya44 card.
|
||||
|
||||
Mic/Line input: if switch is is on, input jack 1/2 is microphone input (mono), otherwise line input (stereo).
|
||||
|
||||
Bypass: analogue bypass from ADC input to output for channel 1+2. Same as "Monitor" in the windows driver.
|
||||
Bypass 1: same for channel 3+4.
|
||||
|
||||
Crossmix: cross-mixer from channels 1+2 to channels 3+4
|
||||
Crossmix 1: cross-mixer from channels 3+4 to channels 1+2
|
||||
|
||||
IEC958 Output: switch for S/PDIF output.
|
||||
This is not supported by the ESI windows driver.
|
||||
S/PDIF should output the same signal as channel 3+4. [untested!]
|
||||
|
||||
|
||||
Digitial output selectors:
|
||||
|
||||
These switches allow a direct digital routing from the ADCs to the DACs.
|
||||
Each switch determines where the digital input data to one of the DACs comes from.
|
||||
They are not supported by the ESI windows driver.
|
||||
For normal operation, they should all be set to "PCM out".
|
||||
|
||||
H/W: Output source channel 1
|
||||
H/W 1: Output source channel 2
|
||||
H/W 2: Output source channel 3
|
||||
H/W 3: Output source channel 4
|
||||
|
||||
H/W 4 ... H/W 9: unknown function, left in to enable testing.
|
||||
Possibly some of these control S/PDIF output(s).
|
||||
If these turn out to be unused, they will go away in later driver versions.
|
||||
|
||||
Selectable values for each of the digital output selectors are:
|
||||
"PCM out" -> DAC output of the corresponding channel (default setting)
|
||||
"Input 1"...
|
||||
"Input 4" -> direct routing from ADC output of the selected input channel
|
||||
|
||||
|
||||
--------
|
||||
|
||||
Feb 14, 2008
|
||||
Rainer Zimmermann
|
||||
mail@lightshed.de
|
||||
|
|
@ -62,6 +62,7 @@ Audio DAPM widgets fall into a number of types:-
|
|||
o Mic - Mic (and optional Jack)
|
||||
o Line - Line Input/Output (and optional Jack)
|
||||
o Speaker - Speaker
|
||||
o Supply - Power or clock supply widget used by other widgets.
|
||||
o Pre - Special PRE widget (exec before all others)
|
||||
o Post - Special POST widget (exec after all others)
|
||||
|
||||
|
|
|
@ -4603,7 +4603,8 @@ F: drivers/pcmcia/pxa2xx*
|
|||
F: drivers/spi/pxa2xx*
|
||||
F: drivers/usb/gadget/pxa2*
|
||||
F: include/sound/pxa2xx-lib.h
|
||||
F: sound/soc/pxa/pxa2xx*
|
||||
F: sound/arm/pxa*
|
||||
F: sound/soc/pxa
|
||||
|
||||
PXA168 SUPPORT
|
||||
P: Eric Miao
|
||||
|
@ -5331,11 +5332,12 @@ P: Liam Girdwood
|
|||
M: lrg@slimlogic.co.uk
|
||||
P: Mark Brown
|
||||
M: broonie@opensource.wolfsonmicro.com
|
||||
T: git git://opensource.wolfsonmicro.com/linux-2.6-asoc
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6.git
|
||||
L: alsa-devel@alsa-project.org (subscribers-only)
|
||||
W: http://alsa-project.org/main/index.php/ASoC
|
||||
S: Supported
|
||||
F: sound/soc/
|
||||
F: include/sound/soc*
|
||||
|
||||
SPARC + UltraSPARC (sparc/sparc64)
|
||||
P: David S. Miller
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
#define MPC52xx_PSC_MAXNUM 6
|
||||
|
||||
/* Programmable Serial Controller (PSC) status register bits */
|
||||
#define MPC52xx_PSC_SR_UNEX_RX 0x0001
|
||||
#define MPC52xx_PSC_SR_DATA_VAL 0x0002
|
||||
#define MPC52xx_PSC_SR_DATA_OVR 0x0004
|
||||
#define MPC52xx_PSC_SR_CMDSEND 0x0008
|
||||
#define MPC52xx_PSC_SR_CDE 0x0080
|
||||
#define MPC52xx_PSC_SR_RXRDY 0x0100
|
||||
#define MPC52xx_PSC_SR_RXFULL 0x0200
|
||||
|
@ -61,6 +65,12 @@
|
|||
#define MPC52xx_PSC_RXTX_FIFO_EMPTY 0x0001
|
||||
|
||||
/* PSC interrupt status/mask bits */
|
||||
#define MPC52xx_PSC_IMR_UNEX_RX_SLOT 0x0001
|
||||
#define MPC52xx_PSC_IMR_DATA_VALID 0x0002
|
||||
#define MPC52xx_PSC_IMR_DATA_OVR 0x0004
|
||||
#define MPC52xx_PSC_IMR_CMD_SEND 0x0008
|
||||
#define MPC52xx_PSC_IMR_ERROR 0x0040
|
||||
#define MPC52xx_PSC_IMR_DEOF 0x0080
|
||||
#define MPC52xx_PSC_IMR_TXRDY 0x0100
|
||||
#define MPC52xx_PSC_IMR_RXRDY 0x0200
|
||||
#define MPC52xx_PSC_IMR_DB 0x0400
|
||||
|
@ -117,6 +127,7 @@
|
|||
#define MPC52xx_PSC_SICR_SIM_FIR (0x6 << 24)
|
||||
#define MPC52xx_PSC_SICR_SIM_CODEC_24 (0x7 << 24)
|
||||
#define MPC52xx_PSC_SICR_SIM_CODEC_32 (0xf << 24)
|
||||
#define MPC52xx_PSC_SICR_AWR (1 << 30)
|
||||
#define MPC52xx_PSC_SICR_GENCLK (1 << 23)
|
||||
#define MPC52xx_PSC_SICR_I2S (1 << 22)
|
||||
#define MPC52xx_PSC_SICR_CLKPOL (1 << 21)
|
||||
|
|
|
@ -1005,6 +1005,7 @@
|
|||
#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196
|
||||
#define PCI_DEVICE_ID_PLX_9030 0x9030
|
||||
#define PCI_DEVICE_ID_PLX_9050 0x9050
|
||||
#define PCI_DEVICE_ID_PLX_9056 0x9056
|
||||
#define PCI_DEVICE_ID_PLX_9080 0x9080
|
||||
#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001
|
||||
|
||||
|
@ -1314,6 +1315,13 @@
|
|||
|
||||
#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */
|
||||
#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
|
||||
#define PCI_DEVICE_ID_CREATIVE_20K1 0x0005
|
||||
#define PCI_DEVICE_ID_CREATIVE_20K2 0x000b
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB0760 0x0024
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08801 0x0041
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08802 0x0042
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_SB08803 0x0043
|
||||
#define PCI_SUBDEVICE_ID_CREATIVE_HENDRIX 0x6000
|
||||
|
||||
#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */
|
||||
#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938
|
||||
|
@ -1847,6 +1855,10 @@
|
|||
#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107
|
||||
#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108
|
||||
|
||||
#define PCI_VENDOR_ID_DIGIGRAM 0x1369
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM 0xc001
|
||||
#define PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM 0xc002
|
||||
|
||||
#define PCI_VENDOR_ID_KAWASAKI 0x136b
|
||||
#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01
|
||||
|
||||
|
|
|
@ -255,6 +255,7 @@ typedef int __bitwise snd_pcm_subformat_t;
|
|||
#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */
|
||||
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */
|
||||
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */
|
||||
#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */
|
||||
|
||||
typedef int __bitwise snd_pcm_state_t;
|
||||
#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */
|
||||
|
|
|
@ -300,19 +300,10 @@ int snd_card_create(int idx, const char *id,
|
|||
struct module *module, int extra_size,
|
||||
struct snd_card **card_ret);
|
||||
|
||||
static inline __deprecated
|
||||
struct snd_card *snd_card_new(int idx, const char *id,
|
||||
struct module *module, int extra_size)
|
||||
{
|
||||
struct snd_card *card;
|
||||
if (snd_card_create(idx, id, module, extra_size, &card) < 0)
|
||||
return NULL;
|
||||
return card;
|
||||
}
|
||||
|
||||
int snd_card_disconnect(struct snd_card *card);
|
||||
int snd_card_free(struct snd_card *card);
|
||||
int snd_card_free_when_closed(struct snd_card *card);
|
||||
void snd_card_set_id(struct snd_card *card, const char *id);
|
||||
int snd_card_register(struct snd_card *card);
|
||||
int snd_card_info_init(void);
|
||||
int snd_card_info_done(void);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#warning "This file is deprecated"
|
|
@ -98,6 +98,7 @@ struct snd_pcm_ops {
|
|||
#define SNDRV_PCM_IOCTL1_INFO 1
|
||||
#define SNDRV_PCM_IOCTL1_CHANNEL_INFO 2
|
||||
#define SNDRV_PCM_IOCTL1_GSTATE 3
|
||||
#define SNDRV_PCM_IOCTL1_FIFO_SIZE 4
|
||||
|
||||
#define SNDRV_PCM_TRIGGER_STOP 0
|
||||
#define SNDRV_PCM_TRIGGER_START 1
|
||||
|
@ -270,6 +271,7 @@ struct snd_pcm_runtime {
|
|||
snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
|
||||
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
|
||||
unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */
|
||||
snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */
|
||||
|
||||
/* -- HW params -- */
|
||||
snd_pcm_access_t access; /* access mode */
|
||||
|
@ -486,80 +488,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream);
|
|||
void snd_pcm_vma_notify_data(void *client, void *data);
|
||||
int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
|
||||
|
||||
#if BITS_PER_LONG >= 64
|
||||
|
||||
static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
|
||||
{
|
||||
*rem = *n % div;
|
||||
*n /= div;
|
||||
}
|
||||
|
||||
#elif defined(i386)
|
||||
|
||||
static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
|
||||
{
|
||||
u_int32_t low, high;
|
||||
low = *n & 0xffffffff;
|
||||
high = *n >> 32;
|
||||
if (high) {
|
||||
u_int32_t high1 = high % div;
|
||||
high /= div;
|
||||
asm("divl %2":"=a" (low), "=d" (*rem):"rm" (div), "a" (low), "d" (high1));
|
||||
*n = (u_int64_t)high << 32 | low;
|
||||
} else {
|
||||
*n = low / div;
|
||||
*rem = low % div;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void divl(u_int32_t high, u_int32_t low,
|
||||
u_int32_t div,
|
||||
u_int32_t *q, u_int32_t *r)
|
||||
{
|
||||
u_int64_t n = (u_int64_t)high << 32 | low;
|
||||
u_int64_t d = (u_int64_t)div << 31;
|
||||
u_int32_t q1 = 0;
|
||||
int c = 32;
|
||||
while (n > 0xffffffffU) {
|
||||
q1 <<= 1;
|
||||
if (n >= d) {
|
||||
n -= d;
|
||||
q1 |= 1;
|
||||
}
|
||||
d >>= 1;
|
||||
c--;
|
||||
}
|
||||
q1 <<= c;
|
||||
if (n) {
|
||||
low = n;
|
||||
*q = q1 | (low / div);
|
||||
*r = low % div;
|
||||
} else {
|
||||
*r = 0;
|
||||
*q = q1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem)
|
||||
{
|
||||
u_int32_t low, high;
|
||||
low = *n & 0xffffffff;
|
||||
high = *n >> 32;
|
||||
if (high) {
|
||||
u_int32_t high1 = high % div;
|
||||
u_int32_t low1 = low;
|
||||
high /= div;
|
||||
divl(high1, low1, div, &low, rem);
|
||||
*n = (u_int64_t)high << 32 | low;
|
||||
} else {
|
||||
*n = low / div;
|
||||
*rem = low % div;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PCM library
|
||||
*/
|
||||
|
|
|
@ -44,24 +44,6 @@ struct snd_pcm_substream;
|
|||
#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
|
||||
#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated */
|
||||
|
||||
/*
|
||||
* DAI Left/Right Clocks.
|
||||
*
|
||||
* Specifies whether the DAI can support different samples for similtanious
|
||||
* playback and capture. This usually requires a seperate physical frame
|
||||
* clock for playback and capture.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
|
||||
#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
|
||||
|
||||
/*
|
||||
* TDM
|
||||
*
|
||||
* Time Division Multiplexing. Allows PCM data to be multplexed with other
|
||||
* data on the DAI.
|
||||
*/
|
||||
#define SND_SOC_DAIFMT_TDM (1 << 6)
|
||||
|
||||
/*
|
||||
* DAI hardware signal inversions.
|
||||
*
|
||||
|
@ -96,6 +78,10 @@ struct snd_pcm_substream;
|
|||
#define SND_SOC_CLOCK_IN 0
|
||||
#define SND_SOC_CLOCK_OUT 1
|
||||
|
||||
#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_BE)
|
||||
|
||||
struct snd_soc_dai_ops;
|
||||
struct snd_soc_dai;
|
||||
struct snd_ac97_bus_ops;
|
||||
|
@ -208,6 +194,7 @@ struct snd_soc_dai {
|
|||
/* DAI capabilities */
|
||||
struct snd_soc_pcm_stream capture;
|
||||
struct snd_soc_pcm_stream playback;
|
||||
unsigned int symmetric_rates:1;
|
||||
|
||||
/* DAI runtime info */
|
||||
struct snd_pcm_runtime *runtime;
|
||||
|
@ -219,11 +206,8 @@ struct snd_soc_dai {
|
|||
/* DAI private data */
|
||||
void *private_data;
|
||||
|
||||
/* parent codec/platform */
|
||||
union {
|
||||
struct snd_soc_codec *codec;
|
||||
struct snd_soc_platform *platform;
|
||||
};
|
||||
/* parent platform */
|
||||
struct snd_soc_platform *platform;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
|
|
@ -140,16 +140,30 @@
|
|||
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert}
|
||||
#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
|
||||
wevent, wflags) \
|
||||
{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert, \
|
||||
.event = wevent, .event_flags = wflags}
|
||||
#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
|
||||
{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert}
|
||||
#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
|
||||
wevent, wflags) \
|
||||
{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert, \
|
||||
.event = wevent, .event_flags = wflags}
|
||||
|
||||
/* generic register modifier widget */
|
||||
/* generic widgets */
|
||||
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
|
||||
{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \
|
||||
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
|
||||
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
|
||||
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
|
||||
#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
|
||||
{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \
|
||||
.shift = wshift, .invert = winvert, .event = wevent, \
|
||||
.event_flags = wflags}
|
||||
|
||||
/* dapm kcontrol types */
|
||||
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
|
||||
|
@ -265,8 +279,6 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
|
|||
/* dapm events */
|
||||
int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
|
||||
int event);
|
||||
int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
|
||||
enum snd_soc_bias_level level);
|
||||
|
||||
/* dapm sys fs - used by the core */
|
||||
int snd_soc_dapm_sys_add(struct device *dev);
|
||||
|
@ -298,6 +310,7 @@ enum snd_soc_dapm_type {
|
|||
snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */
|
||||
snd_soc_dapm_pre, /* machine specific pre widget - exec first */
|
||||
snd_soc_dapm_post, /* machine specific post widget - exec last */
|
||||
snd_soc_dapm_supply, /* power/clock supply */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -357,6 +370,8 @@ struct snd_soc_dapm_widget {
|
|||
unsigned char suspend:1; /* was active before suspend */
|
||||
unsigned char pmdown:1; /* waiting for timeout */
|
||||
|
||||
int (*power_check)(struct snd_soc_dapm_widget *w);
|
||||
|
||||
/* external events */
|
||||
unsigned short event_flags; /* flags to specify event types */
|
||||
int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);
|
||||
|
@ -368,6 +383,9 @@ struct snd_soc_dapm_widget {
|
|||
/* widget input and outputs */
|
||||
struct list_head sources;
|
||||
struct list_head sinks;
|
||||
|
||||
/* used during DAPM updates */
|
||||
struct list_head power_list;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -118,6 +118,14 @@
|
|||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
|
||||
#define SOC_DOUBLE_EXT(xname, xreg, shift_left, shift_right, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = (unsigned long)&(struct soc_mixer_control) \
|
||||
{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
|
||||
.max = xmax, .invert = xinvert} }
|
||||
#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put, tlv_array) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
|
@ -206,10 +214,6 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
|
|||
struct snd_soc_jack_gpio *gpios);
|
||||
#endif
|
||||
|
||||
/* codec IO */
|
||||
#define snd_soc_read(codec, reg) codec->read(codec, reg)
|
||||
#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
|
||||
|
||||
/* codec register bit access */
|
||||
int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
|
||||
unsigned short mask, unsigned short value);
|
||||
|
@ -331,6 +335,7 @@ struct snd_soc_codec {
|
|||
struct module *owner;
|
||||
struct mutex mutex;
|
||||
struct device *dev;
|
||||
struct snd_soc_device *socdev;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
|
@ -364,6 +369,8 @@ struct snd_soc_codec {
|
|||
enum snd_soc_bias_level bias_level;
|
||||
enum snd_soc_bias_level suspend_bias_level;
|
||||
struct delayed_work delayed_work;
|
||||
struct list_head up_list;
|
||||
struct list_head down_list;
|
||||
|
||||
/* codec DAI's */
|
||||
struct snd_soc_dai *dai;
|
||||
|
@ -417,6 +424,12 @@ struct snd_soc_dai_link {
|
|||
/* codec/machine specific init - e.g. add machine controls */
|
||||
int (*init)(struct snd_soc_codec *codec);
|
||||
|
||||
/* Symmetry requirements */
|
||||
unsigned int symmetric_rates:1;
|
||||
|
||||
/* Symmetry data - only valid if symmetry is being enforced */
|
||||
unsigned int rate;
|
||||
|
||||
/* DAI pcm */
|
||||
struct snd_pcm *pcm;
|
||||
};
|
||||
|
@ -490,6 +503,19 @@ struct soc_enum {
|
|||
void *dapm;
|
||||
};
|
||||
|
||||
/* codec IO */
|
||||
static inline unsigned int snd_soc_read(struct snd_soc_codec *codec,
|
||||
unsigned int reg)
|
||||
{
|
||||
return codec->read(codec, reg);
|
||||
}
|
||||
|
||||
static inline unsigned int snd_soc_write(struct snd_soc_codec *codec,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
return codec->write(codec, reg, val);
|
||||
}
|
||||
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#endif
|
||||
|
|
25
include/sound/wm9081.h
Normal file
25
include/sound/wm9081.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* linux/sound/wm9081.h -- Platform data for WM9081
|
||||
*
|
||||
* Copyright 2009 Wolfson Microelectronics. PLC.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SND_WM_9081_H
|
||||
#define __LINUX_SND_WM_9081_H
|
||||
|
||||
struct wm9081_retune_mobile_setting {
|
||||
const char *name;
|
||||
unsigned int rate;
|
||||
u16 config[20];
|
||||
};
|
||||
|
||||
struct wm9081_retune_mobile_config {
|
||||
struct wm9081_retune_mobile_setting *configs;
|
||||
int num_configs;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1037,7 +1037,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
|
|||
}
|
||||
ldev->selfptr_headphone.ptr = ldev;
|
||||
ldev->selfptr_lineout.ptr = ldev;
|
||||
sdev->ofdev.dev.driver_data = ldev;
|
||||
dev_set_drvdata(&sdev->ofdev.dev, ldev);
|
||||
list_add(&ldev->list, &layouts_list);
|
||||
layouts_list_items++;
|
||||
|
||||
|
@ -1081,7 +1081,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
|
|||
|
||||
static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
|
||||
{
|
||||
struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
|
||||
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
|
||||
int i;
|
||||
|
||||
for (i=0; i<MAX_CODECS_PER_BUS; i++) {
|
||||
|
@ -1114,7 +1114,7 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
|
|||
#ifdef CONFIG_PM
|
||||
static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
|
||||
{
|
||||
struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
|
||||
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
|
||||
|
||||
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
|
||||
ldev->gpio.methods->all_amps_off(&ldev->gpio);
|
||||
|
@ -1124,7 +1124,7 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
|
|||
|
||||
static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
|
||||
{
|
||||
struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
|
||||
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
|
||||
|
||||
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
|
||||
ldev->gpio.methods->all_amps_restore(&ldev->gpio);
|
||||
|
|
|
@ -358,14 +358,14 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev->ofdev.dev.driver_data = control;
|
||||
dev_set_drvdata(&dev->ofdev.dev, control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2sbus_remove(struct macio_dev* dev)
|
||||
{
|
||||
struct i2sbus_control *control = dev->ofdev.dev.driver_data;
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct i2sbus_dev *i2sdev, *tmp;
|
||||
|
||||
list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
|
||||
|
@ -377,7 +377,7 @@ static int i2sbus_remove(struct macio_dev* dev)
|
|||
#ifdef CONFIG_PM
|
||||
static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
|
||||
{
|
||||
struct i2sbus_control *control = dev->ofdev.dev.driver_data;
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct codec_info_item *cii;
|
||||
struct i2sbus_dev* i2sdev;
|
||||
int err, ret = 0;
|
||||
|
@ -407,7 +407,7 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
|
|||
|
||||
static int i2sbus_resume(struct macio_dev* dev)
|
||||
{
|
||||
struct i2sbus_control *control = dev->ofdev.dev.driver_data;
|
||||
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
|
||||
struct codec_info_item *cii;
|
||||
struct i2sbus_dev* i2sdev;
|
||||
int err, ret = 0;
|
||||
|
|
|
@ -205,3 +205,5 @@ config SND_PCM_XRUN_DEBUG
|
|||
|
||||
config SND_VMASTER
|
||||
bool
|
||||
|
||||
source "sound/core/seq/Kconfig"
|
||||
|
|
|
@ -152,15 +152,8 @@ int snd_card_create(int idx, const char *xid,
|
|||
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
if (xid) {
|
||||
if (!snd_info_check_reserved_words(xid)) {
|
||||
snd_printk(KERN_ERR
|
||||
"given id string '%s' is reserved.\n", xid);
|
||||
err = -EBUSY;
|
||||
goto __error;
|
||||
}
|
||||
if (xid)
|
||||
strlcpy(card->id, xid, sizeof(card->id));
|
||||
}
|
||||
err = 0;
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if (idx < 0) {
|
||||
|
@ -483,22 +476,28 @@ int snd_card_free(struct snd_card *card)
|
|||
|
||||
EXPORT_SYMBOL(snd_card_free);
|
||||
|
||||
static void choose_default_id(struct snd_card *card)
|
||||
static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
|
||||
{
|
||||
int i, len, idx_flag = 0, loops = SNDRV_CARDS;
|
||||
char *id, *spos;
|
||||
const char *spos, *src;
|
||||
char *id;
|
||||
|
||||
id = spos = card->shortname;
|
||||
while (*id != '\0') {
|
||||
if (*id == ' ')
|
||||
spos = id + 1;
|
||||
id++;
|
||||
if (nid == NULL) {
|
||||
id = card->shortname;
|
||||
spos = src = id;
|
||||
while (*id != '\0') {
|
||||
if (*id == ' ')
|
||||
spos = id + 1;
|
||||
id++;
|
||||
}
|
||||
} else {
|
||||
spos = src = nid;
|
||||
}
|
||||
id = card->id;
|
||||
while (*spos != '\0' && !isalnum(*spos))
|
||||
spos++;
|
||||
if (isdigit(*spos))
|
||||
*id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
|
||||
*id++ = isalpha(src[0]) ? src[0] : 'D';
|
||||
while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
|
||||
if (isalnum(*spos))
|
||||
*id++ = *spos;
|
||||
|
@ -513,7 +512,7 @@ static void choose_default_id(struct snd_card *card)
|
|||
|
||||
while (1) {
|
||||
if (loops-- == 0) {
|
||||
snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
|
||||
snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
|
||||
strcpy(card->id, card->proc_root->name);
|
||||
return;
|
||||
}
|
||||
|
@ -539,14 +538,33 @@ static void choose_default_id(struct snd_card *card)
|
|||
spos = id + len - 2;
|
||||
if ((size_t)len <= sizeof(card->id) - 2)
|
||||
spos++;
|
||||
*spos++ = '_';
|
||||
*spos++ = '1';
|
||||
*spos++ = '\0';
|
||||
*(char *)spos++ = '_';
|
||||
*(char *)spos++ = '1';
|
||||
*(char *)spos++ = '\0';
|
||||
idx_flag++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_set_id - set card identification name
|
||||
* @card: soundcard structure
|
||||
* @nid: new identification string
|
||||
*
|
||||
* This function sets the card identification and checks for name
|
||||
* collisions.
|
||||
*/
|
||||
void snd_card_set_id(struct snd_card *card, const char *nid)
|
||||
{
|
||||
/* check if user specified own card->id */
|
||||
if (card->id[0] != '\0')
|
||||
return;
|
||||
mutex_lock(&snd_card_mutex);
|
||||
snd_card_set_id_no_lock(card, nid);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_card_set_id);
|
||||
|
||||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
static ssize_t
|
||||
card_id_show_attr(struct device *dev,
|
||||
|
@ -640,8 +658,7 @@ int snd_card_register(struct snd_card *card)
|
|||
mutex_unlock(&snd_card_mutex);
|
||||
return 0;
|
||||
}
|
||||
if (card->id[0] == '\0')
|
||||
choose_default_id(card);
|
||||
snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
|
||||
snd_cards[card->number] = card;
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
init_info_for_card(card);
|
||||
|
|
|
@ -63,7 +63,7 @@ static int snd_jack_dev_register(struct snd_device *device)
|
|||
|
||||
/* Default to the sound card device. */
|
||||
if (!jack->input_dev->dev.parent)
|
||||
jack->input_dev->dev.parent = card->dev;
|
||||
jack->input_dev->dev.parent = snd_card_get_device_link(card);
|
||||
|
||||
err = input_register_device(jack->input_dev);
|
||||
if (err == 0)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/string.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/minors.h>
|
||||
|
@ -617,9 +618,7 @@ static long snd_pcm_oss_bytes(struct snd_pcm_substream *substream, long frames)
|
|||
#else
|
||||
{
|
||||
u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
|
||||
u32 rem;
|
||||
div64_32(&bsize, buffer_size, &rem);
|
||||
return (long)bsize;
|
||||
return div_u64(bsize, buffer_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/math64.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/info.h>
|
||||
|
@ -126,24 +127,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
|
||||
#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
|
||||
#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask))
|
||||
#else
|
||||
#define xrun_debug(substream) 0
|
||||
#define xrun_debug(substream, mask) 0
|
||||
#endif
|
||||
|
||||
#define dump_stack_on_xrun(substream) do { \
|
||||
if (xrun_debug(substream) > 1) \
|
||||
dump_stack(); \
|
||||
#define dump_stack_on_xrun(substream) do { \
|
||||
if (xrun_debug(substream, 2)) \
|
||||
dump_stack(); \
|
||||
} while (0)
|
||||
|
||||
static void pcm_debug_name(struct snd_pcm_substream *substream,
|
||||
char *name, size_t len)
|
||||
{
|
||||
snprintf(name, len, "pcmC%dD%d%c:%d",
|
||||
substream->pcm->card->number,
|
||||
substream->pcm->device,
|
||||
substream->stream ? 'c' : 'p',
|
||||
substream->number);
|
||||
}
|
||||
|
||||
static void xrun(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
|
||||
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
|
||||
if (xrun_debug(substream)) {
|
||||
snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
|
||||
substream->pcm->card->number,
|
||||
substream->pcm->device,
|
||||
substream->stream ? 'c' : 'p');
|
||||
if (xrun_debug(substream, 1)) {
|
||||
char name[16];
|
||||
pcm_debug_name(substream, name, sizeof(name));
|
||||
snd_printd(KERN_DEBUG "XRUN: %s\n", name);
|
||||
dump_stack_on_xrun(substream);
|
||||
}
|
||||
}
|
||||
|
@ -154,16 +168,16 @@ snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
|
|||
{
|
||||
snd_pcm_uframes_t pos;
|
||||
|
||||
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
|
||||
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
|
||||
pos = substream->ops->pointer(substream);
|
||||
if (pos == SNDRV_PCM_POS_XRUN)
|
||||
return pos; /* XRUN */
|
||||
if (pos >= runtime->buffer_size) {
|
||||
if (printk_ratelimit()) {
|
||||
snd_printd(KERN_ERR "BUG: stream = %i, pos = 0x%lx, "
|
||||
char name[16];
|
||||
pcm_debug_name(substream, name, sizeof(name));
|
||||
snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, "
|
||||
"buffer size = 0x%lx, period size = 0x%lx\n",
|
||||
substream->stream, pos, runtime->buffer_size,
|
||||
name, pos, runtime->buffer_size,
|
||||
runtime->period_size);
|
||||
}
|
||||
pos = 0;
|
||||
|
@ -197,7 +211,7 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
|
|||
|
||||
#define hw_ptr_error(substream, fmt, args...) \
|
||||
do { \
|
||||
if (xrun_debug(substream)) { \
|
||||
if (xrun_debug(substream, 1)) { \
|
||||
if (printk_ratelimit()) { \
|
||||
snd_printd("PCM: " fmt, ##args); \
|
||||
} \
|
||||
|
@ -251,7 +265,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
|
|||
}
|
||||
|
||||
/* Do jiffies check only in xrun_debug mode */
|
||||
if (!xrun_debug(substream))
|
||||
if (!xrun_debug(substream, 4))
|
||||
goto no_jiffies_check;
|
||||
|
||||
/* Skip the jiffies check for hardwares with BATCH flag.
|
||||
|
@ -261,6 +275,9 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
|
|||
if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
|
||||
goto no_jiffies_check;
|
||||
hdelta = new_hw_ptr - old_hw_ptr;
|
||||
if (hdelta < runtime->delay)
|
||||
goto no_jiffies_check;
|
||||
hdelta -= runtime->delay;
|
||||
jdelta = jiffies - runtime->hw_ptr_jiffies;
|
||||
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
|
||||
delta = jdelta /
|
||||
|
@ -294,14 +311,20 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
|
|||
hw_ptr_interrupt =
|
||||
new_hw_ptr - new_hw_ptr % runtime->period_size;
|
||||
}
|
||||
runtime->hw_ptr_interrupt = hw_ptr_interrupt;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||
runtime->silence_size > 0)
|
||||
snd_pcm_playback_silence(substream, new_hw_ptr);
|
||||
|
||||
if (runtime->status->hw_ptr == new_hw_ptr)
|
||||
return 0;
|
||||
|
||||
runtime->hw_ptr_base = hw_base;
|
||||
runtime->status->hw_ptr = new_hw_ptr;
|
||||
runtime->hw_ptr_jiffies = jiffies;
|
||||
runtime->hw_ptr_interrupt = hw_ptr_interrupt;
|
||||
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
|
||||
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
|
||||
|
||||
return snd_pcm_update_hw_ptr_post(substream, runtime);
|
||||
}
|
||||
|
@ -342,8 +365,12 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
|
|||
new_hw_ptr = hw_base + pos;
|
||||
}
|
||||
/* Do jiffies check only in xrun_debug mode */
|
||||
if (xrun_debug(substream) &&
|
||||
((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
|
||||
if (!xrun_debug(substream, 4))
|
||||
goto no_jiffies_check;
|
||||
if (delta < runtime->delay)
|
||||
goto no_jiffies_check;
|
||||
delta -= runtime->delay;
|
||||
if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
|
||||
hw_ptr_error(substream,
|
||||
"hw_ptr skipping! "
|
||||
"(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
|
||||
|
@ -352,13 +379,19 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
|
|||
((delta * HZ) / runtime->rate));
|
||||
return 0;
|
||||
}
|
||||
no_jiffies_check:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||
runtime->silence_size > 0)
|
||||
snd_pcm_playback_silence(substream, new_hw_ptr);
|
||||
|
||||
if (runtime->status->hw_ptr == new_hw_ptr)
|
||||
return 0;
|
||||
|
||||
runtime->hw_ptr_base = hw_base;
|
||||
runtime->status->hw_ptr = new_hw_ptr;
|
||||
runtime->hw_ptr_jiffies = jiffies;
|
||||
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
|
||||
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
|
||||
|
||||
return snd_pcm_update_hw_ptr_post(substream, runtime);
|
||||
}
|
||||
|
@ -452,7 +485,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
|
|||
*r = 0;
|
||||
return UINT_MAX;
|
||||
}
|
||||
div64_32(&n, c, r);
|
||||
n = div_u64_rem(n, c, r);
|
||||
if (n >= UINT_MAX) {
|
||||
*r = 0;
|
||||
return UINT_MAX;
|
||||
|
@ -1524,6 +1557,23 @@ static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
|
||||
void *arg)
|
||||
{
|
||||
struct snd_pcm_hw_params *params = arg;
|
||||
snd_pcm_format_t format;
|
||||
int channels, width;
|
||||
|
||||
params->fifo_size = substream->runtime->hw.fifo_size;
|
||||
if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
|
||||
format = params_format(params);
|
||||
channels = params_channels(params);
|
||||
width = snd_pcm_format_physical_width(format);
|
||||
params->fifo_size /= width * channels;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
|
||||
* @substream: the pcm substream instance
|
||||
|
@ -1545,6 +1595,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
|||
return snd_pcm_lib_ioctl_reset(substream, arg);
|
||||
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
|
||||
return snd_pcm_lib_ioctl_channel_info(substream, arg);
|
||||
case SNDRV_PCM_IOCTL1_FIFO_SIZE:
|
||||
return snd_pcm_lib_ioctl_fifo_size(substream, arg);
|
||||
}
|
||||
return -ENXIO;
|
||||
}
|
||||
|
|
|
@ -312,9 +312,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
|
|||
|
||||
hw = &substream->runtime->hw;
|
||||
if (!params->info)
|
||||
params->info = hw->info;
|
||||
if (!params->fifo_size)
|
||||
params->fifo_size = hw->fifo_size;
|
||||
params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
|
||||
if (!params->fifo_size) {
|
||||
if (snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
|
||||
snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
|
||||
snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
|
||||
snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) {
|
||||
changed = substream->ops->ioctl(substream,
|
||||
SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
|
||||
if (params < 0)
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
params->rmask = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -587,14 +596,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
|
|||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
status->avail = snd_pcm_playback_avail(runtime);
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
|
||||
runtime->status->state == SNDRV_PCM_STATE_DRAINING)
|
||||
runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
|
||||
status->delay = runtime->buffer_size - status->avail;
|
||||
else
|
||||
status->delay += runtime->delay;
|
||||
} else
|
||||
status->delay = 0;
|
||||
} else {
|
||||
status->avail = snd_pcm_capture_avail(runtime);
|
||||
if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
|
||||
status->delay = status->avail;
|
||||
status->delay = status->avail + runtime->delay;
|
||||
else
|
||||
status->delay = 0;
|
||||
}
|
||||
|
@ -2410,6 +2420,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
|
|||
n = snd_pcm_playback_hw_avail(runtime);
|
||||
else
|
||||
n = snd_pcm_capture_avail(runtime);
|
||||
n += runtime->delay;
|
||||
break;
|
||||
case SNDRV_PCM_STATE_XRUN:
|
||||
err = -EPIPE;
|
||||
|
|
16
sound/core/seq/Kconfig
Normal file
16
sound/core/seq/Kconfig
Normal file
|
@ -0,0 +1,16 @@
|
|||
# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
|
||||
|
||||
config SND_RAWMIDI_SEQ
|
||||
def_tristate SND_SEQUENCER && SND_RAWMIDI
|
||||
|
||||
config SND_OPL3_LIB_SEQ
|
||||
def_tristate SND_SEQUENCER && SND_OPL3_LIB
|
||||
|
||||
config SND_OPL4_LIB_SEQ
|
||||
def_tristate SND_SEQUENCER && SND_OPL4_LIB
|
||||
|
||||
config SND_SBAWE_SEQ
|
||||
def_tristate SND_SEQUENCER && SND_SBAWE
|
||||
|
||||
config SND_EMU10K1_SEQ
|
||||
def_tristate SND_SEQUENCER && SND_EMU10K1
|
|
@ -17,14 +17,6 @@ snd-seq-midi-event-objs := seq_midi_event.o
|
|||
snd-seq-dummy-objs := seq_dummy.o
|
||||
snd-seq-virmidi-objs := seq_virmidi.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
|
||||
ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
|
||||
obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
|
||||
|
@ -33,8 +25,8 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
|
|||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
|
||||
obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
|
||||
obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
|
||||
obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
|
||||
|
|
|
@ -7,14 +7,6 @@ snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
|
|||
snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
|
||||
snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
|
||||
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o
|
||||
obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-opl3-synth.o
|
||||
|
|
|
@ -6,13 +6,5 @@
|
|||
snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
|
||||
snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o
|
||||
obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
|
||||
|
|
|
@ -177,15 +177,18 @@ config SND_ES18XX
|
|||
will be called snd-es18xx.
|
||||
|
||||
config SND_SC6000
|
||||
tristate "Gallant SC-6000, Audio Excel DSP 16"
|
||||
tristate "Gallant SC-6000/6600/7000 and Audio Excel DSP 16"
|
||||
depends on HAS_IOPORT
|
||||
select SND_WSS_LIB
|
||||
select SND_OPL3_LIB
|
||||
select SND_MPU401_UART
|
||||
help
|
||||
Say Y here to include support for Gallant SC-6000 card and clones:
|
||||
Say Y here to include support for Gallant SC-6000, SC-6600, SC-7000
|
||||
cards and clones:
|
||||
Audio Excel DSP 16 and Zoltrix AV302.
|
||||
|
||||
These cards are based on CompuMedia ASC-9308 or ASC-9408 chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-sc6000.
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ static int __devexit snd_es1688_remove(struct device *dev, unsigned int n)
|
|||
static struct isa_driver snd_es1688_driver = {
|
||||
.match = snd_es1688_match,
|
||||
.probe = snd_es1688_probe,
|
||||
.remove = snd_es1688_remove,
|
||||
.remove = __devexit_p(snd_es1688_remove),
|
||||
#if 0 /* FIXME */
|
||||
.suspend = snd_es1688_suspend,
|
||||
.resume = snd_es1688_resume,
|
||||
|
|
|
@ -348,7 +348,7 @@ static int __devexit snd_gusextreme_remove(struct device *dev, unsigned int n)
|
|||
static struct isa_driver snd_gusextreme_driver = {
|
||||
.match = snd_gusextreme_match,
|
||||
.probe = snd_gusextreme_probe,
|
||||
.remove = snd_gusextreme_remove,
|
||||
.remove = __devexit_p(snd_gusextreme_remove),
|
||||
#if 0 /* FIXME */
|
||||
.suspend = snd_gusextreme_suspend,
|
||||
.resume = snd_gusextreme_resume,
|
||||
|
|
|
@ -13,14 +13,6 @@ snd-sbawe-objs := sbawe.o emu8000.o
|
|||
snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
|
||||
snd-es968-objs := es968.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
|
||||
obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
|
||||
|
@ -33,4 +25,4 @@ ifeq ($(CONFIG_SND_SB16_CSP),y)
|
|||
obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
|
||||
obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
|
||||
endif
|
||||
obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
|
||||
obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emu8000-synth.o
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* Driver for Gallant SC-6000 soundcard. This card is also known as
|
||||
* Audio Excel DSP 16 or Zoltrix AV302.
|
||||
* These cards use CompuMedia ASC-9308 chip + AD1848 codec.
|
||||
* SC-6600 and SC-7000 cards are also supported. They are based on
|
||||
* CompuMedia ASC-9408 chip and CS4231 codec.
|
||||
*
|
||||
* Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
|
||||
*
|
||||
|
@ -54,6 +56,7 @@ static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|||
/* 0x300, 0x310, 0x320, 0x330 */
|
||||
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */
|
||||
static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */
|
||||
static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
|
||||
|
@ -73,6 +76,8 @@ module_param_array(mpu_irq, int, NULL, 0444);
|
|||
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
|
||||
module_param_array(dma, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
|
||||
module_param_array(joystick, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(joystick, "Enable gameport.");
|
||||
|
||||
/*
|
||||
* Commands of SC6000's DSP (SBPRO+special).
|
||||
|
@ -191,7 +196,7 @@ static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
|
|||
return val;
|
||||
}
|
||||
|
||||
static __devinit int sc6000_wait_data(char __iomem *vport)
|
||||
static int sc6000_wait_data(char __iomem *vport)
|
||||
{
|
||||
int loop = 1000;
|
||||
unsigned char val = 0;
|
||||
|
@ -206,7 +211,7 @@ static __devinit int sc6000_wait_data(char __iomem *vport)
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static __devinit int sc6000_read(char __iomem *vport)
|
||||
static int sc6000_read(char __iomem *vport)
|
||||
{
|
||||
if (sc6000_wait_data(vport))
|
||||
return -EBUSY;
|
||||
|
@ -215,7 +220,7 @@ static __devinit int sc6000_read(char __iomem *vport)
|
|||
|
||||
}
|
||||
|
||||
static __devinit int sc6000_write(char __iomem *vport, int cmd)
|
||||
static int sc6000_write(char __iomem *vport, int cmd)
|
||||
{
|
||||
unsigned char val;
|
||||
int loop = 500000;
|
||||
|
@ -276,8 +281,33 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport)
|
|||
}
|
||||
|
||||
/* detection and initialization */
|
||||
static int __devinit sc6000_cfg_write(char __iomem *vport,
|
||||
unsigned char softcfg)
|
||||
static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
|
||||
{
|
||||
if (sc6000_write(vport, COMMAND_6C) < 0) {
|
||||
snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
|
||||
return -EIO;
|
||||
}
|
||||
if (sc6000_write(vport, COMMAND_5C) < 0) {
|
||||
snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
|
||||
return -EIO;
|
||||
}
|
||||
if (sc6000_write(vport, cfg[0]) < 0) {
|
||||
snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
|
||||
return -EIO;
|
||||
}
|
||||
if (sc6000_write(vport, cfg[1]) < 0) {
|
||||
snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
|
||||
return -EIO;
|
||||
}
|
||||
if (sc6000_write(vport, COMMAND_C5) < 0) {
|
||||
snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
|
||||
{
|
||||
|
||||
if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
|
||||
|
@ -291,7 +321,7 @@ static int __devinit sc6000_cfg_write(char __iomem *vport,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit sc6000_setup_board(char __iomem *vport, int config)
|
||||
static int sc6000_setup_board(char __iomem *vport, int config)
|
||||
{
|
||||
int loop = 10;
|
||||
|
||||
|
@ -334,16 +364,39 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
|
||||
char __iomem *vmss_port, int mpu_irq)
|
||||
static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
|
||||
long xport, long xmpu,
|
||||
long xmss_port, int joystick)
|
||||
{
|
||||
cfg[0] = 0;
|
||||
cfg[1] = 0;
|
||||
if (xport == 0x240)
|
||||
cfg[0] |= 1;
|
||||
if (xmpu != SNDRV_AUTO_PORT) {
|
||||
cfg[0] |= (xmpu & 0x30) >> 2;
|
||||
cfg[1] |= 0x20;
|
||||
}
|
||||
if (xmss_port == 0xe80)
|
||||
cfg[0] |= 0x10;
|
||||
cfg[0] |= 0x40; /* always set */
|
||||
if (!joystick)
|
||||
cfg[0] |= 0x02;
|
||||
cfg[1] |= 0x80; /* enable WSS system */
|
||||
cfg[1] &= ~0x40; /* disable IDE */
|
||||
snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
|
||||
}
|
||||
|
||||
static int __devinit sc6000_init_board(char __iomem *vport,
|
||||
char __iomem *vmss_port, int dev)
|
||||
{
|
||||
char answer[15];
|
||||
char version[2];
|
||||
int mss_config = sc6000_irq_to_softcfg(irq) |
|
||||
sc6000_dma_to_softcfg(dma);
|
||||
int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
|
||||
sc6000_dma_to_softcfg(dma[dev]);
|
||||
int config = mss_config |
|
||||
sc6000_mpu_irq_to_softcfg(mpu_irq);
|
||||
sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
|
||||
int err;
|
||||
int old = 0;
|
||||
|
||||
err = sc6000_dsp_reset(vport);
|
||||
if (err < 0) {
|
||||
|
@ -360,7 +413,6 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
|
|||
/*
|
||||
* My SC-6000 card return "SC-6000" in DSPCopyright, so
|
||||
* if we have something different, we have to be warned.
|
||||
* Mine returns "SC-6000A " - KH
|
||||
*/
|
||||
if (strncmp("SC-6000", answer, 7))
|
||||
snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
|
||||
|
@ -372,13 +424,32 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
|
|||
printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
|
||||
answer, version[0], version[1]);
|
||||
|
||||
/*
|
||||
* 0x0A == (IRQ 7, DMA 1, MIRQ 0)
|
||||
*/
|
||||
err = sc6000_cfg_write(vport, 0x0a);
|
||||
/* set configuration */
|
||||
sc6000_write(vport, COMMAND_5C);
|
||||
if (sc6000_read(vport) < 0)
|
||||
old = 1;
|
||||
|
||||
if (!old) {
|
||||
int cfg[2];
|
||||
sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
|
||||
mss_port[dev], joystick[dev]);
|
||||
if (sc6000_hw_cfg_write(vport, cfg) < 0) {
|
||||
snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
err = sc6000_setup_board(vport, config);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
|
||||
return -EFAULT;
|
||||
snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sc6000_dsp_reset(vport);
|
||||
|
||||
if (!old) {
|
||||
sc6000_write(vport, COMMAND_60);
|
||||
sc6000_write(vport, 0x02);
|
||||
sc6000_dsp_reset(vport);
|
||||
}
|
||||
|
||||
err = sc6000_setup_board(vport, config);
|
||||
|
@ -386,10 +457,9 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
|
|||
snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = sc6000_init_mss(vport, config, vmss_port, mss_config);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "Can not initialize "
|
||||
snd_printk(KERN_ERR "Cannot initialize "
|
||||
"Microsoft Sound System mode.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -485,14 +555,16 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
struct snd_card *card;
|
||||
struct snd_wss *chip;
|
||||
struct snd_opl3 *opl3;
|
||||
char __iomem *vport;
|
||||
char __iomem **vport;
|
||||
char __iomem *vmss_port;
|
||||
|
||||
|
||||
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
|
||||
err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport),
|
||||
&card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
vport = card->private_data;
|
||||
if (xirq == SNDRV_AUTO_IRQ) {
|
||||
xirq = snd_legacy_find_free_irq(possible_irqs);
|
||||
if (xirq < 0) {
|
||||
|
@ -517,8 +589,8 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
err = -EBUSY;
|
||||
goto err_exit;
|
||||
}
|
||||
vport = devm_ioport_map(devptr, port[dev], 0x10);
|
||||
if (!vport) {
|
||||
*vport = devm_ioport_map(devptr, port[dev], 0x10);
|
||||
if (*vport == NULL) {
|
||||
snd_printk(KERN_ERR PFX
|
||||
"I/O port cannot be iomaped.\n");
|
||||
err = -EBUSY;
|
||||
|
@ -533,7 +605,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
goto err_unmap1;
|
||||
}
|
||||
vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
|
||||
if (!vport) {
|
||||
if (!vmss_port) {
|
||||
snd_printk(KERN_ERR PFX
|
||||
"MSS port I/O cannot be iomaped.\n");
|
||||
err = -EBUSY;
|
||||
|
@ -544,7 +616,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
port[dev], xirq, xdma,
|
||||
mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
|
||||
|
||||
err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]);
|
||||
err = sc6000_init_board(*vport, vmss_port, dev);
|
||||
if (err < 0)
|
||||
goto err_unmap2;
|
||||
|
||||
|
@ -552,7 +624,6 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
WSS_HW_DETECT, 0, &chip);
|
||||
if (err < 0)
|
||||
goto err_unmap2;
|
||||
card->private_data = chip;
|
||||
|
||||
err = snd_wss_pcm(chip, 0, NULL);
|
||||
if (err < 0) {
|
||||
|
@ -608,6 +679,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
return 0;
|
||||
|
||||
err_unmap2:
|
||||
sc6000_setup_board(*vport, 0);
|
||||
release_region(mss_port[dev], 4);
|
||||
err_unmap1:
|
||||
release_region(port[dev], 0x10);
|
||||
|
@ -618,11 +690,17 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
|
||||
static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev)
|
||||
{
|
||||
struct snd_card *card = dev_get_drvdata(devptr);
|
||||
char __iomem **vport = card->private_data;
|
||||
|
||||
if (sc6000_setup_board(*vport, 0) < 0)
|
||||
snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
|
||||
|
||||
release_region(port[dev], 0x10);
|
||||
release_region(mss_port[dev], 4);
|
||||
|
||||
snd_card_free(dev_get_drvdata(devptr));
|
||||
dev_set_drvdata(devptr, NULL);
|
||||
snd_card_free(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -619,8 +619,7 @@ static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
/* hw_free callback */
|
||||
static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
if (substream->runtime->dma_area)
|
||||
vfree(substream->runtime->dma_area);
|
||||
vfree(substream->runtime->dma_area);
|
||||
substream->runtime->dma_area = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -935,7 +935,7 @@ snd_harmony_create(struct snd_card *card,
|
|||
h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
|
||||
if (h->iobase == NULL) {
|
||||
printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
|
||||
padev->hpa.start);
|
||||
(unsigned long)padev->hpa.start);
|
||||
err = -EBUSY;
|
||||
goto free_and_ret;
|
||||
}
|
||||
|
@ -1020,7 +1020,7 @@ static struct parisc_driver snd_harmony_driver = {
|
|||
.name = "harmony",
|
||||
.id_table = snd_harmony_devtable,
|
||||
.probe = snd_harmony_probe,
|
||||
.remove = snd_harmony_remove,
|
||||
.remove = __devexit_p(snd_harmony_remove),
|
||||
};
|
||||
|
||||
static int __init
|
||||
|
|
|
@ -275,6 +275,16 @@ config SND_CS5535AUDIO
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-cs5535audio.
|
||||
|
||||
config SND_CTXFI
|
||||
tristate "Creative Sound Blaster X-Fi"
|
||||
select SND_PCM
|
||||
help
|
||||
If you want to use soundcards based on Creative Sound Blastr X-Fi
|
||||
boards with 20k1 or 20k2 chips, say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-ctxfi.
|
||||
|
||||
config SND_DARLA20
|
||||
tristate "(Echoaudio) Darla20"
|
||||
select FW_LOADER
|
||||
|
@ -532,6 +542,9 @@ config SND_HDSP
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-hdsp.
|
||||
|
||||
comment "Don't forget to add built-in firmwares for HDSP driver"
|
||||
depends on SND_HDSP=y
|
||||
|
||||
config SND_HDSPM
|
||||
tristate "RME Hammerfall DSP MADI"
|
||||
select SND_HWDEP
|
||||
|
@ -622,6 +635,16 @@ config SND_KORG1212
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-korg1212.
|
||||
|
||||
config SND_LX6464ES
|
||||
tristate "Digigram LX6464ES"
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y here to include support for Digigram LX6464ES boards.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-lx6464es.
|
||||
|
||||
|
||||
config SND_MAESTRO3
|
||||
tristate "ESS Allegro/Maestro3"
|
||||
select SND_AC97_CODEC
|
||||
|
@ -764,8 +787,8 @@ config SND_VIRTUOSO
|
|||
select SND_OXYGEN_LIB
|
||||
help
|
||||
Say Y here to include support for sound cards based on the
|
||||
Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and
|
||||
Essence STX.
|
||||
Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
|
||||
Essence ST (Deluxe), and Essence STX.
|
||||
Support for the HDAV1.3 (Deluxe) is very experimental.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
|
|
|
@ -59,9 +59,11 @@ obj-$(CONFIG_SND) += \
|
|||
ali5451/ \
|
||||
au88x0/ \
|
||||
aw2/ \
|
||||
ctxfi/ \
|
||||
ca0106/ \
|
||||
cs46xx/ \
|
||||
cs5535audio/ \
|
||||
lx6464es/ \
|
||||
echoaudio/ \
|
||||
emu10k1/ \
|
||||
hda/ \
|
||||
|
|
|
@ -1255,8 +1255,8 @@ static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
|
|||
int temp;
|
||||
|
||||
temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
|
||||
temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
|
||||
return (temp);
|
||||
temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
|
||||
return temp;
|
||||
}
|
||||
|
||||
static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
|
||||
|
@ -1504,8 +1504,7 @@ static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
|
|||
int temp;
|
||||
|
||||
temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
|
||||
//temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
|
||||
temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes));
|
||||
temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
@ -2441,7 +2440,8 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
|
|||
spin_lock(&vortex->lock);
|
||||
for (i = 0; i < NR_ADB; i++) {
|
||||
if (vortex->dma_adb[i].fifo_status == FIFO_START) {
|
||||
if (vortex_adbdma_bufshift(vortex, i)) ;
|
||||
if (!vortex_adbdma_bufshift(vortex, i))
|
||||
continue;
|
||||
spin_unlock(&vortex->lock);
|
||||
snd_pcm_period_elapsed(vortex->dma_adb[i].
|
||||
substream);
|
||||
|
|
|
@ -810,6 +810,8 @@ static struct pci_device_id snd_bt87x_ids[] = {
|
|||
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC),
|
||||
/* Voodoo TV 200 */
|
||||
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC),
|
||||
/* Askey Computer Corp. MagicTView'99 */
|
||||
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x144f, 0x3000, GENERIC),
|
||||
/* AVerMedia Studio No. 103, 203, ...? */
|
||||
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, AVPHONE98),
|
||||
/* Prolink PixelView PV-M4900 */
|
||||
|
|
|
@ -1319,7 +1319,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
|
|||
}
|
||||
|
||||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
strcpy(pcm->name, "CA0106");
|
||||
|
||||
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
|
|
|
@ -739,7 +739,7 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch
|
|||
} while (0)
|
||||
|
||||
static __devinitdata
|
||||
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
|
||||
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
|
||||
|
||||
static char *slave_vols[] __devinitdata = {
|
||||
"Analog Front Playback Volume",
|
||||
|
@ -841,6 +841,9 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
|
|||
snd_ca0106_master_db_scale);
|
||||
if (!vmaster)
|
||||
return -ENOMEM;
|
||||
err = snd_ctl_add(card, vmaster);
|
||||
if (err < 0)
|
||||
return err;
|
||||
add_slaves(card, vmaster, slave_vols);
|
||||
|
||||
if (emu->details->spi_dac == 1) {
|
||||
|
@ -848,8 +851,13 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
|
|||
NULL);
|
||||
if (!vmaster)
|
||||
return -ENOMEM;
|
||||
err = snd_ctl_add(card, vmaster);
|
||||
if (err < 0)
|
||||
return err;
|
||||
add_slaves(card, vmaster, slave_sws);
|
||||
}
|
||||
|
||||
strcpy(card->mixername, "CA0106");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
5
sound/pci/ctxfi/Makefile
Normal file
5
sound/pci/ctxfi/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
|
||||
ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
|
||||
cthw20k2.o cthw20k1.o
|
||||
|
||||
obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
|
636
sound/pci/ctxfi/ct20k1reg.h
Normal file
636
sound/pci/ctxfi/ct20k1reg.h
Normal file
|
@ -0,0 +1,636 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*/
|
||||
|
||||
#ifndef CT20K1REG_H
|
||||
#define CT20k1REG_H
|
||||
|
||||
/* 20k1 registers */
|
||||
#define DSPXRAM_START 0x000000
|
||||
#define DSPXRAM_END 0x013FFC
|
||||
#define DSPAXRAM_START 0x020000
|
||||
#define DSPAXRAM_END 0x023FFC
|
||||
#define DSPYRAM_START 0x040000
|
||||
#define DSPYRAM_END 0x04FFFC
|
||||
#define DSPAYRAM_START 0x020000
|
||||
#define DSPAYRAM_END 0x063FFC
|
||||
#define DSPMICRO_START 0x080000
|
||||
#define DSPMICRO_END 0x0B3FFC
|
||||
#define DSP0IO_START 0x100000
|
||||
#define DSP0IO_END 0x101FFC
|
||||
#define AUDIORINGIPDSP0_START 0x100000
|
||||
#define AUDIORINGIPDSP0_END 0x1003FC
|
||||
#define AUDIORINGOPDSP0_START 0x100400
|
||||
#define AUDIORINGOPDSP0_END 0x1007FC
|
||||
#define AUDPARARINGIODSP0_START 0x100800
|
||||
#define AUDPARARINGIODSP0_END 0x100BFC
|
||||
#define DSP0LOCALHWREG_START 0x100C00
|
||||
#define DSP0LOCALHWREG_END 0x100C3C
|
||||
#define DSP0XYRAMAGINDEX_START 0x100C40
|
||||
#define DSP0XYRAMAGINDEX_END 0x100C5C
|
||||
#define DSP0XYRAMAGMDFR_START 0x100C60
|
||||
#define DSP0XYRAMAGMDFR_END 0x100C7C
|
||||
#define DSP0INTCONTLVEC_START 0x100C80
|
||||
#define DSP0INTCONTLVEC_END 0x100CD8
|
||||
#define INTCONTLGLOBALREG_START 0x100D1C
|
||||
#define INTCONTLGLOBALREG_END 0x100D3C
|
||||
#define HOSTINTFPORTADDRCONTDSP0 0x100D40
|
||||
#define HOSTINTFPORTDATADSP0 0x100D44
|
||||
#define TIME0PERENBDSP0 0x100D60
|
||||
#define TIME0COUNTERDSP0 0x100D64
|
||||
#define TIME1PERENBDSP0 0x100D68
|
||||
#define TIME1COUNTERDSP0 0x100D6C
|
||||
#define TIME2PERENBDSP0 0x100D70
|
||||
#define TIME2COUNTERDSP0 0x100D74
|
||||
#define TIME3PERENBDSP0 0x100D78
|
||||
#define TIME3COUNTERDSP0 0x100D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP0 0x100D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP0 0x100D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP0 0x100DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP0 0x100DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP0 0x100DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP0 0x100DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP0 0x100DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP0 0x100DFC
|
||||
#define DSP0CONDCODE 0x100E00
|
||||
#define DSP0STACKFLAG 0x100E04
|
||||
#define DSP0PROGCOUNTSTACKPTREG 0x100E08
|
||||
#define DSP0PROGCOUNTSTACKDATAREG 0x100E0C
|
||||
#define DSP0CURLOOPADDRREG 0x100E10
|
||||
#define DSP0CURLOOPCOUNT 0x100E14
|
||||
#define DSP0TOPLOOPCOUNTSTACK 0x100E18
|
||||
#define DSP0TOPLOOPADDRSTACK 0x100E1C
|
||||
#define DSP0LOOPSTACKPTR 0x100E20
|
||||
#define DSP0STASSTACKDATAREG 0x100E24
|
||||
#define DSP0STASSTACKPTR 0x100E28
|
||||
#define DSP0PROGCOUNT 0x100E2C
|
||||
#define GLOBDSPDEBGREG 0x100E30
|
||||
#define GLOBDSPBREPTRREG 0x100E30
|
||||
#define DSP0XYRAMBASE_START 0x100EA0
|
||||
#define DSP0XYRAMBASE_END 0x100EBC
|
||||
#define DSP0XYRAMLENG_START 0x100EC0
|
||||
#define DSP0XYRAMLENG_END 0x100EDC
|
||||
#define SEMAPHOREREGDSP0 0x100EE0
|
||||
#define DSP0INTCONTMASKREG 0x100EE4
|
||||
#define DSP0INTCONTPENDREG 0x100EE8
|
||||
#define DSP0INTCONTSERVINT 0x100EEC
|
||||
#define DSPINTCONTEXTINTMODREG 0x100EEC
|
||||
#define GPIODSP0 0x100EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP0 0x100F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP0 0x100F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP0 0x100F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP0 0x100F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP0 0x100F40
|
||||
#define DMADSPCURADDRREG_ENDDSP0 0x100F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP0 0x100F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP0 0x100F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP0 0x100F80
|
||||
#define DMATANXCOUNTREG_ENDDSP0 0x100F9C
|
||||
#define DMATIMEBUGREG_STARTDSP0 0x100FA0
|
||||
#define DMATIMEBUGREG_ENDDSP0 0x100FAC
|
||||
#define DMACNTLMODFREG_STARTDSP0 0x100FA0
|
||||
#define DMACNTLMODFREG_ENDDSP0 0x100FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP0 0x100FEC
|
||||
#define DSP0XGPRAM_START 0x101000
|
||||
#define DSP0XGPRAM_END 0x1017FC
|
||||
#define DSP0YGPRAM_START 0x101800
|
||||
#define DSP0YGPRAM_END 0x101FFC
|
||||
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP1_START 0x102000
|
||||
#define AUDIORINGIPDSP1_END 0x1023FC
|
||||
#define AUDIORINGOPDSP1_START 0x102400
|
||||
#define AUDIORINGOPDSP1_END 0x1027FC
|
||||
#define AUDPARARINGIODSP1_START 0x102800
|
||||
#define AUDPARARINGIODSP1_END 0x102BFC
|
||||
#define DSP1LOCALHWREG_START 0x102C00
|
||||
#define DSP1LOCALHWREG_END 0x102C3C
|
||||
#define DSP1XYRAMAGINDEX_START 0x102C40
|
||||
#define DSP1XYRAMAGINDEX_END 0x102C5C
|
||||
#define DSP1XYRAMAGMDFR_START 0x102C60
|
||||
#define DSP1XYRAMAGMDFR_END 0x102C7C
|
||||
#define DSP1INTCONTLVEC_START 0x102C80
|
||||
#define DSP1INTCONTLVEC_END 0x102CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP1 0x102D40
|
||||
#define HOSTINTFPORTDATADSP1 0x102D44
|
||||
#define TIME0PERENBDSP1 0x102D60
|
||||
#define TIME0COUNTERDSP1 0x102D64
|
||||
#define TIME1PERENBDSP1 0x102D68
|
||||
#define TIME1COUNTERDSP1 0x102D6C
|
||||
#define TIME2PERENBDSP1 0x102D70
|
||||
#define TIME2COUNTERDSP1 0x102D74
|
||||
#define TIME3PERENBDSP1 0x102D78
|
||||
#define TIME3COUNTERDSP1 0x102D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP1 0x102D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP1 0x102D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP1 0x102DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP1 0x102DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP1 0x102DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP1 0x102DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP1 0x102DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP1 0x102DFC
|
||||
|
||||
#define DSP1CONDCODE 0x102E00
|
||||
#define DSP1STACKFLAG 0x102E04
|
||||
#define DSP1PROGCOUNTSTACKPTREG 0x102E08
|
||||
#define DSP1PROGCOUNTSTACKDATAREG 0x102E0C
|
||||
#define DSP1CURLOOPADDRREG 0x102E10
|
||||
#define DSP1CURLOOPCOUNT 0x102E14
|
||||
#define DSP1TOPLOOPCOUNTSTACK 0x102E18
|
||||
#define DSP1TOPLOOPADDRSTACK 0x102E1C
|
||||
#define DSP1LOOPSTACKPTR 0x102E20
|
||||
#define DSP1STASSTACKDATAREG 0x102E24
|
||||
#define DSP1STASSTACKPTR 0x102E28
|
||||
#define DSP1PROGCOUNT 0x102E2C
|
||||
#define DSP1XYRAMBASE_START 0x102EA0
|
||||
#define DSP1XYRAMBASE_END 0x102EBC
|
||||
#define DSP1XYRAMLENG_START 0x102EC0
|
||||
#define DSP1XYRAMLENG_END 0x102EDC
|
||||
#define SEMAPHOREREGDSP1 0x102EE0
|
||||
#define DSP1INTCONTMASKREG 0x102EE4
|
||||
#define DSP1INTCONTPENDREG 0x102EE8
|
||||
#define DSP1INTCONTSERVINT 0x102EEC
|
||||
#define GPIODSP1 0x102EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP1 0x102F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP1 0x102F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP1 0x102F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP1 0x102F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP1 0x102F40
|
||||
#define DMADSPCURADDRREG_ENDDSP1 0x102F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP1 0x102F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP1 0x102F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP1 0x102F80
|
||||
#define DMATANXCOUNTREG_ENDDSP1 0x102F9C
|
||||
#define DMATIMEBUGREG_STARTDSP1 0x102FA0
|
||||
#define DMATIMEBUGREG_ENDDSP1 0x102FAC
|
||||
#define DMACNTLMODFREG_STARTDSP1 0x102FA0
|
||||
#define DMACNTLMODFREG_ENDDSP1 0x102FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP1 0x102FEC
|
||||
#define DSP1XGPRAM_START 0x103000
|
||||
#define DSP1XGPRAM_END 0x1033FC
|
||||
#define DSP1YGPRAM_START 0x103400
|
||||
#define DSP1YGPRAM_END 0x1037FC
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP2_START 0x104000
|
||||
#define AUDIORINGIPDSP2_END 0x1043FC
|
||||
#define AUDIORINGOPDSP2_START 0x104400
|
||||
#define AUDIORINGOPDSP2_END 0x1047FC
|
||||
#define AUDPARARINGIODSP2_START 0x104800
|
||||
#define AUDPARARINGIODSP2_END 0x104BFC
|
||||
#define DSP2LOCALHWREG_START 0x104C00
|
||||
#define DSP2LOCALHWREG_END 0x104C3C
|
||||
#define DSP2XYRAMAGINDEX_START 0x104C40
|
||||
#define DSP2XYRAMAGINDEX_END 0x104C5C
|
||||
#define DSP2XYRAMAGMDFR_START 0x104C60
|
||||
#define DSP2XYRAMAGMDFR_END 0x104C7C
|
||||
#define DSP2INTCONTLVEC_START 0x104C80
|
||||
#define DSP2INTCONTLVEC_END 0x104CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP2 0x104D40
|
||||
#define HOSTINTFPORTDATADSP2 0x104D44
|
||||
#define TIME0PERENBDSP2 0x104D60
|
||||
#define TIME0COUNTERDSP2 0x104D64
|
||||
#define TIME1PERENBDSP2 0x104D68
|
||||
#define TIME1COUNTERDSP2 0x104D6C
|
||||
#define TIME2PERENBDSP2 0x104D70
|
||||
#define TIME2COUNTERDSP2 0x104D74
|
||||
#define TIME3PERENBDSP2 0x104D78
|
||||
#define TIME3COUNTERDSP2 0x104D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP2 0x104D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP2 0x104D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP2 0x104DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP2 0x104DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP2 0x104DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP2 0x104DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP2 0x104DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP2 0x104DFC
|
||||
#define DSP2CONDCODE 0x104E00
|
||||
#define DSP2STACKFLAG 0x104E04
|
||||
#define DSP2PROGCOUNTSTACKPTREG 0x104E08
|
||||
#define DSP2PROGCOUNTSTACKDATAREG 0x104E0C
|
||||
#define DSP2CURLOOPADDRREG 0x104E10
|
||||
#define DSP2CURLOOPCOUNT 0x104E14
|
||||
#define DSP2TOPLOOPCOUNTSTACK 0x104E18
|
||||
#define DSP2TOPLOOPADDRSTACK 0x104E1C
|
||||
#define DSP2LOOPSTACKPTR 0x104E20
|
||||
#define DSP2STASSTACKDATAREG 0x104E24
|
||||
#define DSP2STASSTACKPTR 0x104E28
|
||||
#define DSP2PROGCOUNT 0x104E2C
|
||||
#define DSP2XYRAMBASE_START 0x104EA0
|
||||
#define DSP2XYRAMBASE_END 0x104EBC
|
||||
#define DSP2XYRAMLENG_START 0x104EC0
|
||||
#define DSP2XYRAMLENG_END 0x104EDC
|
||||
#define SEMAPHOREREGDSP2 0x104EE0
|
||||
#define DSP2INTCONTMASKREG 0x104EE4
|
||||
#define DSP2INTCONTPENDREG 0x104EE8
|
||||
#define DSP2INTCONTSERVINT 0x104EEC
|
||||
#define GPIODSP2 0x104EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP2 0x104F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP2 0x104F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP2 0x104F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP2 0x104F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP2 0x104F40
|
||||
#define DMADSPCURADDRREG_ENDDSP2 0x104F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP2 0x104F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP2 0x104F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP2 0x104F80
|
||||
#define DMATANXCOUNTREG_ENDDSP2 0x104F9C
|
||||
#define DMATIMEBUGREG_STARTDSP2 0x104FA0
|
||||
#define DMATIMEBUGREG_ENDDSP2 0x104FAC
|
||||
#define DMACNTLMODFREG_STARTDSP2 0x104FA0
|
||||
#define DMACNTLMODFREG_ENDDSP2 0x104FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP2 0x104FEC
|
||||
#define DSP2XGPRAM_START 0x105000
|
||||
#define DSP2XGPRAM_END 0x1051FC
|
||||
#define DSP2YGPRAM_START 0x105800
|
||||
#define DSP2YGPRAM_END 0x1059FC
|
||||
|
||||
|
||||
|
||||
#define AUDIORINGIPDSP3_START 0x106000
|
||||
#define AUDIORINGIPDSP3_END 0x1063FC
|
||||
#define AUDIORINGOPDSP3_START 0x106400
|
||||
#define AUDIORINGOPDSP3_END 0x1067FC
|
||||
#define AUDPARARINGIODSP3_START 0x106800
|
||||
#define AUDPARARINGIODSP3_END 0x106BFC
|
||||
#define DSP3LOCALHWREG_START 0x106C00
|
||||
#define DSP3LOCALHWREG_END 0x106C3C
|
||||
#define DSP3XYRAMAGINDEX_START 0x106C40
|
||||
#define DSP3XYRAMAGINDEX_END 0x106C5C
|
||||
#define DSP3XYRAMAGMDFR_START 0x106C60
|
||||
#define DSP3XYRAMAGMDFR_END 0x106C7C
|
||||
#define DSP3INTCONTLVEC_START 0x106C80
|
||||
#define DSP3INTCONTLVEC_END 0x106CD8
|
||||
#define HOSTINTFPORTADDRCONTDSP3 0x106D40
|
||||
#define HOSTINTFPORTDATADSP3 0x106D44
|
||||
#define TIME0PERENBDSP3 0x106D60
|
||||
#define TIME0COUNTERDSP3 0x106D64
|
||||
#define TIME1PERENBDSP3 0x106D68
|
||||
#define TIME1COUNTERDSP3 0x106D6C
|
||||
#define TIME2PERENBDSP3 0x106D70
|
||||
#define TIME2COUNTERDSP3 0x106D74
|
||||
#define TIME3PERENBDSP3 0x106D78
|
||||
#define TIME3COUNTERDSP3 0x106D7C
|
||||
#define XRAMINDOPERREFNOUP_STARTDSP3 0x106D80
|
||||
#define XRAMINDOPERREFNOUP_ENDDSP3 0x106D9C
|
||||
#define XRAMINDOPERREFUP_STARTDSP3 0x106DA0
|
||||
#define XRAMINDOPERREFUP_ENDDSP3 0x106DBC
|
||||
#define YRAMINDOPERREFNOUP_STARTDSP3 0x106DC0
|
||||
#define YRAMINDOPERREFNOUP_ENDDSP3 0x106DDC
|
||||
#define YRAMINDOPERREFUP_STARTDSP3 0x106DE0
|
||||
#define YRAMINDOPERREFUP_ENDDSP3 0x100DFC
|
||||
|
||||
#define DSP3CONDCODE 0x106E00
|
||||
#define DSP3STACKFLAG 0x106E04
|
||||
#define DSP3PROGCOUNTSTACKPTREG 0x106E08
|
||||
#define DSP3PROGCOUNTSTACKDATAREG 0x106E0C
|
||||
#define DSP3CURLOOPADDRREG 0x106E10
|
||||
#define DSP3CURLOOPCOUNT 0x106E14
|
||||
#define DSP3TOPLOOPCOUNTSTACK 0x106E18
|
||||
#define DSP3TOPLOOPADDRSTACK 0x106E1C
|
||||
#define DSP3LOOPSTACKPTR 0x106E20
|
||||
#define DSP3STASSTACKDATAREG 0x106E24
|
||||
#define DSP3STASSTACKPTR 0x106E28
|
||||
#define DSP3PROGCOUNT 0x106E2C
|
||||
#define DSP3XYRAMBASE_START 0x106EA0
|
||||
#define DSP3XYRAMBASE_END 0x106EBC
|
||||
#define DSP3XYRAMLENG_START 0x106EC0
|
||||
#define DSP3XYRAMLENG_END 0x106EDC
|
||||
#define SEMAPHOREREGDSP3 0x106EE0
|
||||
#define DSP3INTCONTMASKREG 0x106EE4
|
||||
#define DSP3INTCONTPENDREG 0x106EE8
|
||||
#define DSP3INTCONTSERVINT 0x106EEC
|
||||
#define GPIODSP3 0x106EFC
|
||||
#define DMADSPBASEADDRREG_STARTDSP3 0x106F00
|
||||
#define DMADSPBASEADDRREG_ENDDSP3 0x106F1C
|
||||
#define DMAHOSTBASEADDRREG_STARTDSP3 0x106F20
|
||||
#define DMAHOSTBASEADDRREG_ENDDSP3 0x106F3C
|
||||
#define DMADSPCURADDRREG_STARTDSP3 0x106F40
|
||||
#define DMADSPCURADDRREG_ENDDSP3 0x106F5C
|
||||
#define DMAHOSTCURADDRREG_STARTDSP3 0x106F60
|
||||
#define DMAHOSTCURADDRREG_ENDDSP3 0x106F7C
|
||||
#define DMATANXCOUNTREG_STARTDSP3 0x106F80
|
||||
#define DMATANXCOUNTREG_ENDDSP3 0x106F9C
|
||||
#define DMATIMEBUGREG_STARTDSP3 0x106FA0
|
||||
#define DMATIMEBUGREG_ENDDSP3 0x106FAC
|
||||
#define DMACNTLMODFREG_STARTDSP3 0x106FA0
|
||||
#define DMACNTLMODFREG_ENDDSP3 0x106FAC
|
||||
|
||||
#define DMAGLOBSTATSREGDSP3 0x106FEC
|
||||
#define DSP3XGPRAM_START 0x107000
|
||||
#define DSP3XGPRAM_END 0x1071FC
|
||||
#define DSP3YGPRAM_START 0x107800
|
||||
#define DSP3YGPRAM_END 0x1079FC
|
||||
|
||||
/* end of DSP reg definitions */
|
||||
|
||||
#define DSPAIMAP_START 0x108000
|
||||
#define DSPAIMAP_END 0x1083FC
|
||||
#define DSPPIMAP_START 0x108400
|
||||
#define DSPPIMAP_END 0x1087FC
|
||||
#define DSPPOMAP_START 0x108800
|
||||
#define DSPPOMAP_END 0x108BFC
|
||||
#define DSPPOCTL 0x108C00
|
||||
#define TKCTL_START 0x110000
|
||||
#define TKCTL_END 0x110FFC
|
||||
#define TKCC_START 0x111000
|
||||
#define TKCC_END 0x111FFC
|
||||
#define TKIMAP_START 0x112000
|
||||
#define TKIMAP_END 0x112FFC
|
||||
#define TKDCTR16 0x113000
|
||||
#define TKPB16 0x113004
|
||||
#define TKBS16 0x113008
|
||||
#define TKDCTR32 0x11300C
|
||||
#define TKPB32 0x113010
|
||||
#define TKBS32 0x113014
|
||||
#define ICDCTR16 0x113018
|
||||
#define ITBS16 0x11301C
|
||||
#define ICDCTR32 0x113020
|
||||
#define ITBS32 0x113024
|
||||
#define ITSTART 0x113028
|
||||
#define TKSQ 0x11302C
|
||||
|
||||
#define TKSCCTL_START 0x114000
|
||||
#define TKSCCTL_END 0x11403C
|
||||
#define TKSCADR_START 0x114100
|
||||
#define TKSCADR_END 0x11413C
|
||||
#define TKSCDATAX_START 0x114800
|
||||
#define TKSCDATAX_END 0x1149FC
|
||||
#define TKPCDATAX_START 0x120000
|
||||
#define TKPCDATAX_END 0x12FFFC
|
||||
|
||||
#define MALSA 0x130000
|
||||
#define MAPPHA 0x130004
|
||||
#define MAPPLA 0x130008
|
||||
#define MALSB 0x130010
|
||||
#define MAPPHB 0x130014
|
||||
#define MAPPLB 0x130018
|
||||
|
||||
#define TANSPORTMAPABREGS_START 0x130020
|
||||
#define TANSPORTMAPABREGS_END 0x13A2FC
|
||||
|
||||
#define PTPAHX 0x13B000
|
||||
#define PTPALX 0x13B004
|
||||
|
||||
#define TANSPPAGETABLEPHYADDR015_START 0x13B008
|
||||
#define TANSPPAGETABLEPHYADDR015_END 0x13B07C
|
||||
#define TRNQADRX_START 0x13B100
|
||||
#define TRNQADRX_END 0x13B13C
|
||||
#define TRNQTIMX_START 0x13B200
|
||||
#define TRNQTIMX_END 0x13B23C
|
||||
#define TRNQAPARMX_START 0x13B300
|
||||
#define TRNQAPARMX_END 0x13B33C
|
||||
|
||||
#define TRNQCNT 0x13B400
|
||||
#define TRNCTL 0x13B404
|
||||
#define TRNIS 0x13B408
|
||||
#define TRNCURTS 0x13B40C
|
||||
|
||||
#define AMOP_START 0x140000
|
||||
#define AMOPLO 0x140000
|
||||
#define AMOPHI 0x140004
|
||||
#define AMOP_END 0x147FFC
|
||||
#define PMOP_START 0x148000
|
||||
#define PMOPLO 0x148000
|
||||
#define PMOPHI 0x148004
|
||||
#define PMOP_END 0x14FFFC
|
||||
#define PCURR_START 0x150000
|
||||
#define PCURR_END 0x153FFC
|
||||
#define PTRAG_START 0x154000
|
||||
#define PTRAG_END 0x157FFC
|
||||
#define PSR_START 0x158000
|
||||
#define PSR_END 0x15BFFC
|
||||
|
||||
#define PFSTAT4SEG_START 0x160000
|
||||
#define PFSTAT4SEG_END 0x160BFC
|
||||
#define PFSTAT2SEG_START 0x160C00
|
||||
#define PFSTAT2SEG_END 0x1617FC
|
||||
#define PFTARG4SEG_START 0x164000
|
||||
#define PFTARG4SEG_END 0x164BFC
|
||||
#define PFTARG2SEG_START 0x164C00
|
||||
#define PFTARG2SEG_END 0x1657FC
|
||||
#define PFSR4SEG_START 0x168000
|
||||
#define PFSR4SEG_END 0x168BFC
|
||||
#define PFSR2SEG_START 0x168C00
|
||||
#define PFSR2SEG_END 0x1697FC
|
||||
#define PCURRMS4SEG_START 0x16C000
|
||||
#define PCURRMS4SEG_END 0x16CCFC
|
||||
#define PCURRMS2SEG_START 0x16CC00
|
||||
#define PCURRMS2SEG_END 0x16D7FC
|
||||
#define PTARGMS4SEG_START 0x170000
|
||||
#define PTARGMS4SEG_END 0x172FFC
|
||||
#define PTARGMS2SEG_START 0x173000
|
||||
#define PTARGMS2SEG_END 0x1747FC
|
||||
#define PSRMS4SEG_START 0x170000
|
||||
#define PSRMS4SEG_END 0x172FFC
|
||||
#define PSRMS2SEG_START 0x173000
|
||||
#define PSRMS2SEG_END 0x1747FC
|
||||
|
||||
#define PRING_LO_START 0x190000
|
||||
#define PRING_LO_END 0x193FFC
|
||||
#define PRING_HI_START 0x194000
|
||||
#define PRING_HI_END 0x197FFC
|
||||
#define PRING_LO_HI_START 0x198000
|
||||
#define PRING_LO_HI 0x198000
|
||||
#define PRING_LO_HI_END 0x19BFFC
|
||||
|
||||
#define PINTFIFO 0x1A0000
|
||||
#define SRCCTL 0x1B0000
|
||||
#define SRCCCR 0x1B0004
|
||||
#define SRCIMAP 0x1B0008
|
||||
#define SRCODDC 0x1B000C
|
||||
#define SRCCA 0x1B0010
|
||||
#define SRCCF 0x1B0014
|
||||
#define SRCSA 0x1B0018
|
||||
#define SRCLA 0x1B001C
|
||||
#define SRCCTLSWR 0x1B0020
|
||||
|
||||
/* SRC HERE */
|
||||
#define SRCALBA 0x1B002C
|
||||
#define SRCMCTL 0x1B012C
|
||||
#define SRCCERR 0x1B022C
|
||||
#define SRCITB 0x1B032C
|
||||
#define SRCIPM 0x1B082C
|
||||
#define SRCIP 0x1B102C
|
||||
#define SRCENBSTAT 0x1B202C
|
||||
#define SRCENBLO 0x1B212C
|
||||
#define SRCENBHI 0x1B222C
|
||||
#define SRCENBS 0x1B232C
|
||||
#define SRCENB 0x1B282C
|
||||
#define SRCENB07 0x1B282C
|
||||
#define SRCENBS07 0x1B302C
|
||||
|
||||
#define SRCDN0Z 0x1B0030
|
||||
#define SRCDN0Z0 0x1B0030
|
||||
#define SRCDN0Z1 0x1B0034
|
||||
#define SRCDN0Z2 0x1B0038
|
||||
#define SRCDN0Z3 0x1B003C
|
||||
#define SRCDN1Z 0x1B0040
|
||||
#define SRCDN1Z0 0x1B0040
|
||||
#define SRCDN1Z1 0x1B0044
|
||||
#define SRCDN1Z2 0x1B0048
|
||||
#define SRCDN1Z3 0x1B004C
|
||||
#define SRCDN1Z4 0x1B0050
|
||||
#define SRCDN1Z5 0x1B0054
|
||||
#define SRCDN1Z6 0x1B0058
|
||||
#define SRCDN1Z7 0x1B005C
|
||||
#define SRCUPZ 0x1B0060
|
||||
#define SRCUPZ0 0x1B0060
|
||||
#define SRCUPZ1 0x1B0064
|
||||
#define SRCUPZ2 0x1B0068
|
||||
#define SRCUPZ3 0x1B006C
|
||||
#define SRCUPZ4 0x1B0070
|
||||
#define SRCUPZ5 0x1B0074
|
||||
#define SRCUPZ6 0x1B0078
|
||||
#define SRCUPZ7 0x1B007C
|
||||
#define SRCCD0 0x1B0080
|
||||
#define SRCCD1 0x1B0084
|
||||
#define SRCCD2 0x1B0088
|
||||
#define SRCCD3 0x1B008C
|
||||
#define SRCCD4 0x1B0090
|
||||
#define SRCCD5 0x1B0094
|
||||
#define SRCCD6 0x1B0098
|
||||
#define SRCCD7 0x1B009C
|
||||
#define SRCCD8 0x1B00A0
|
||||
#define SRCCD9 0x1B00A4
|
||||
#define SRCCDA 0x1B00A8
|
||||
#define SRCCDB 0x1B00AC
|
||||
#define SRCCDC 0x1B00B0
|
||||
#define SRCCDD 0x1B00B4
|
||||
#define SRCCDE 0x1B00B8
|
||||
#define SRCCDF 0x1B00BC
|
||||
#define SRCCD10 0x1B00C0
|
||||
#define SRCCD11 0x1B00C4
|
||||
#define SRCCD12 0x1B00C8
|
||||
#define SRCCD13 0x1B00CC
|
||||
#define SRCCD14 0x1B00D0
|
||||
#define SRCCD15 0x1B00D4
|
||||
#define SRCCD16 0x1B00D8
|
||||
#define SRCCD17 0x1B00DC
|
||||
#define SRCCD18 0x1B00E0
|
||||
#define SRCCD19 0x1B00E4
|
||||
#define SRCCD1A 0x1B00E8
|
||||
#define SRCCD1B 0x1B00EC
|
||||
#define SRCCD1C 0x1B00F0
|
||||
#define SRCCD1D 0x1B00F4
|
||||
#define SRCCD1E 0x1B00F8
|
||||
#define SRCCD1F 0x1B00FC
|
||||
|
||||
#define SRCCONTRBLOCK_START 0x1B0100
|
||||
#define SRCCONTRBLOCK_END 0x1BFFFC
|
||||
#define FILTOP_START 0x1C0000
|
||||
#define FILTOP_END 0x1C05FC
|
||||
#define FILTIMAP_START 0x1C0800
|
||||
#define FILTIMAP_END 0x1C0DFC
|
||||
#define FILTZ1_START 0x1C1000
|
||||
#define FILTZ1_END 0x1C15FC
|
||||
#define FILTZ2_START 0x1C1800
|
||||
#define FILTZ2_END 0x1C1DFC
|
||||
#define DAOIMAP_START 0x1C5000
|
||||
#define DAOIMAP 0x1C5000
|
||||
#define DAOIMAP_END 0x1C5124
|
||||
|
||||
#define AC97D 0x1C5400
|
||||
#define AC97A 0x1C5404
|
||||
#define AC97CTL 0x1C5408
|
||||
#define I2SCTL 0x1C5420
|
||||
|
||||
#define SPOS 0x1C5440
|
||||
#define SPOSA 0x1C5440
|
||||
#define SPOSB 0x1C5444
|
||||
#define SPOSC 0x1C5448
|
||||
#define SPOSD 0x1C544C
|
||||
|
||||
#define SPISA 0x1C5450
|
||||
#define SPISB 0x1C5454
|
||||
#define SPISC 0x1C5458
|
||||
#define SPISD 0x1C545C
|
||||
|
||||
#define SPFSCTL 0x1C5460
|
||||
|
||||
#define SPFS0 0x1C5468
|
||||
#define SPFS1 0x1C546C
|
||||
#define SPFS2 0x1C5470
|
||||
#define SPFS3 0x1C5474
|
||||
#define SPFS4 0x1C5478
|
||||
#define SPFS5 0x1C547C
|
||||
|
||||
#define SPOCTL 0x1C5480
|
||||
#define SPICTL 0x1C5484
|
||||
#define SPISTS 0x1C5488
|
||||
#define SPINTP 0x1C548C
|
||||
#define SPINTE 0x1C5490
|
||||
#define SPUTCTLAB 0x1C5494
|
||||
#define SPUTCTLCD 0x1C5498
|
||||
|
||||
#define SRTSPA 0x1C54C0
|
||||
#define SRTSPB 0x1C54C4
|
||||
#define SRTSPC 0x1C54C8
|
||||
#define SRTSPD 0x1C54CC
|
||||
|
||||
#define SRTSCTL 0x1C54D0
|
||||
#define SRTSCTLA 0x1C54D0
|
||||
#define SRTSCTLB 0x1C54D4
|
||||
#define SRTSCTLC 0x1C54D8
|
||||
#define SRTSCTLD 0x1C54DC
|
||||
|
||||
#define SRTI2S 0x1C54E0
|
||||
#define SRTICTL 0x1C54F0
|
||||
|
||||
#define WC 0x1C6000
|
||||
#define TIMR 0x1C6004
|
||||
# define TIMR_IE (1<<15)
|
||||
# define TIMR_IP (1<<14)
|
||||
|
||||
#define GIP 0x1C6010
|
||||
#define GIE 0x1C6014
|
||||
#define DIE 0x1C6018
|
||||
#define DIC 0x1C601C
|
||||
#define GPIO 0x1C6020
|
||||
#define GPIOCTL 0x1C6024
|
||||
#define GPIP 0x1C6028
|
||||
#define GPIE 0x1C602C
|
||||
#define DSPINT0 0x1C6030
|
||||
#define DSPEIOC 0x1C6034
|
||||
#define MUADAT 0x1C6040
|
||||
#define MUACMD 0x1C6044
|
||||
#define MUASTAT 0x1C6044
|
||||
#define MUBDAT 0x1C6048
|
||||
#define MUBCMD 0x1C604C
|
||||
#define MUBSTAT 0x1C604C
|
||||
#define UARTCMA 0x1C6050
|
||||
#define UARTCMB 0x1C6054
|
||||
#define UARTIP 0x1C6058
|
||||
#define UARTIE 0x1C605C
|
||||
#define PLLCTL 0x1C6060
|
||||
#define PLLDCD 0x1C6064
|
||||
#define GCTL 0x1C6070
|
||||
#define ID0 0x1C6080
|
||||
#define ID1 0x1C6084
|
||||
#define ID2 0x1C6088
|
||||
#define ID3 0x1C608C
|
||||
#define SDRCTL 0x1C7000
|
||||
|
||||
|
||||
#define I2SA_L 0x0L
|
||||
#define I2SA_R 0x1L
|
||||
#define I2SB_L 0x8L
|
||||
#define I2SB_R 0x9L
|
||||
#define I2SC_L 0x10L
|
||||
#define I2SC_R 0x11L
|
||||
#define I2SD_L 0x18L
|
||||
#define I2SD_R 0x19L
|
||||
|
||||
#endif /* CT20K1REG_H */
|
||||
|
||||
|
85
sound/pci/ctxfi/ct20k2reg.h
Normal file
85
sound/pci/ctxfi/ct20k2reg.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*/
|
||||
|
||||
#ifndef _20K2REGISTERS_H_
|
||||
#define _20K2REGISTERS_H_
|
||||
|
||||
|
||||
/* Timer Registers */
|
||||
#define TIMER_TIMR 0x1B7004
|
||||
#define INTERRUPT_GIP 0x1B7010
|
||||
#define INTERRUPT_GIE 0x1B7014
|
||||
|
||||
/* I2C Registers */
|
||||
#define I2C_IF_ADDRESS 0x1B9000
|
||||
#define I2C_IF_WDATA 0x1B9004
|
||||
#define I2C_IF_RDATA 0x1B9008
|
||||
#define I2C_IF_STATUS 0x1B900C
|
||||
#define I2C_IF_WLOCK 0x1B9010
|
||||
|
||||
/* Global Control Registers */
|
||||
#define GLOBAL_CNTL_GCTL 0x1B7090
|
||||
|
||||
/* PLL Registers */
|
||||
#define PLL_CTL 0x1B7080
|
||||
#define PLL_STAT 0x1B7084
|
||||
#define PLL_ENB 0x1B7088
|
||||
|
||||
/* SRC Registers */
|
||||
#define SRC_CTL 0x1A0000 /* 0x1A0000 + (256 * Chn) */
|
||||
#define SRC_CCR 0x1A0004 /* 0x1A0004 + (256 * Chn) */
|
||||
#define SRC_IMAP 0x1A0008 /* 0x1A0008 + (256 * Chn) */
|
||||
#define SRC_CA 0x1A0010 /* 0x1A0010 + (256 * Chn) */
|
||||
#define SRC_CF 0x1A0014 /* 0x1A0014 + (256 * Chn) */
|
||||
#define SRC_SA 0x1A0018 /* 0x1A0018 + (256 * Chn) */
|
||||
#define SRC_LA 0x1A001C /* 0x1A001C + (256 * Chn) */
|
||||
#define SRC_CTLSWR 0x1A0020 /* 0x1A0020 + (256 * Chn) */
|
||||
#define SRC_CD 0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */
|
||||
#define SRC_MCTL 0x1A012C
|
||||
#define SRC_IP 0x1A102C /* 0x1A102C + (256 * Regn) */
|
||||
#define SRC_ENB 0x1A282C /* 0x1A282C + (256 * Regn) */
|
||||
#define SRC_ENBSTAT 0x1A202C
|
||||
#define SRC_ENBSA 0x1A232C
|
||||
#define SRC_DN0Z 0x1A0030
|
||||
#define SRC_DN1Z 0x1A0040
|
||||
#define SRC_UPZ 0x1A0060
|
||||
|
||||
/* GPIO Registers */
|
||||
#define GPIO_DATA 0x1B7020
|
||||
#define GPIO_CTRL 0x1B7024
|
||||
|
||||
/* Virtual memory registers */
|
||||
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
|
||||
#define VMEM_PTPAH 0x1C6304 /* 0x1C6304 + (16 * Chn) */
|
||||
#define VMEM_CTL 0x1C7000
|
||||
|
||||
/* Transport Registers */
|
||||
#define TRANSPORT_ENB 0x1B6000
|
||||
#define TRANSPORT_CTL 0x1B6004
|
||||
#define TRANSPORT_INT 0x1B6008
|
||||
|
||||
/* Audio IO */
|
||||
#define AUDIO_IO_AIM 0x1B5000 /* 0x1B5000 + (0x04 * Chn) */
|
||||
#define AUDIO_IO_TX_CTL 0x1B5400 /* 0x1B5400 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */
|
||||
#define AUDIO_IO_RX_CTL 0x1B5410 /* 0x1B5410 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */
|
||||
#define AUDIO_IO_MCLK 0x1B5600
|
||||
#define AUDIO_IO_TX_BLRCLK 0x1B5604
|
||||
#define AUDIO_IO_RX_BLRCLK 0x1B5608
|
||||
|
||||
/* Mixer */
|
||||
#define MIXER_AMOPLO 0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_AMOPHI 0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_PRING_LO_HI 0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */
|
||||
#define MIXER_PMOPLO 0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_PMOPHI 0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */
|
||||
#define MIXER_AR_ENABLE 0x19000C
|
||||
|
||||
#endif
|
488
sound/pci/ctxfi/ctamixer.c
Normal file
488
sound/pci/ctxfi/ctamixer.c
Normal file
|
@ -0,0 +1,488 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctamixer.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of the Audio Mixer
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 21 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctamixer.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define AMIXER_RESOURCE_NUM 256
|
||||
#define SUM_RESOURCE_NUM 256
|
||||
|
||||
#define AMIXER_Y_IMMEDIATE 1
|
||||
|
||||
#define BLANK_SLOT 4094
|
||||
|
||||
static int amixer_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int amixer_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int amixer_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int amixer_output_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (amixer_index(rsc) << 4) + 0x4;
|
||||
}
|
||||
|
||||
static struct rsc_ops amixer_basic_rsc_ops = {
|
||||
.master = amixer_master,
|
||||
.next_conj = amixer_next_conj,
|
||||
.index = amixer_index,
|
||||
.output_slot = amixer_output_slot,
|
||||
};
|
||||
|
||||
static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
|
||||
amixer->input = rsc;
|
||||
if (NULL == rsc)
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
|
||||
else
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
rsc->ops->output_slot(rsc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* y is a 14-bit immediate constant */
|
||||
static int amixer_set_y(struct amixer *amixer, unsigned int y)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_y(amixer->rsc.ctrl_blk, y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
amixer->sum = sum;
|
||||
if (NULL == sum) {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
|
||||
} else {
|
||||
hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
|
||||
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
|
||||
sum->rsc.ops->index(&sum->rsc));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_commit_write(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
unsigned int index;
|
||||
int i;
|
||||
struct rsc *input;
|
||||
struct sum *sum;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
input = amixer->input;
|
||||
sum = amixer->sum;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
for (i = 0; i < amixer->rsc.msr; i++) {
|
||||
hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
|
||||
if (NULL != input) {
|
||||
hw->amixer_set_x(amixer->rsc.ctrl_blk,
|
||||
input->ops->output_slot(input));
|
||||
input->ops->next_conj(input);
|
||||
}
|
||||
if (NULL != sum) {
|
||||
hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
|
||||
sum->rsc.ops->index(&sum->rsc));
|
||||
sum->rsc.ops->next_conj(&sum->rsc);
|
||||
}
|
||||
index = amixer->rsc.ops->output_slot(&amixer->rsc);
|
||||
hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
|
||||
amixer->rsc.ops->next_conj(&amixer->rsc);
|
||||
}
|
||||
amixer->rsc.ops->master(&amixer->rsc);
|
||||
if (NULL != input)
|
||||
input->ops->master(input);
|
||||
|
||||
if (NULL != sum)
|
||||
sum->rsc.ops->master(&sum->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_commit_raw_write(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
unsigned int index;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
index = amixer->rsc.ops->output_slot(&amixer->rsc);
|
||||
hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_get_y(struct amixer *amixer)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = amixer->rsc.hw;
|
||||
return hw->amixer_get_y(amixer->rsc.ctrl_blk);
|
||||
}
|
||||
|
||||
static int amixer_setup(struct amixer *amixer, struct rsc *input,
|
||||
unsigned int scale, struct sum *sum)
|
||||
{
|
||||
amixer_set_input(amixer, input);
|
||||
amixer_set_y(amixer, scale);
|
||||
amixer_set_sum(amixer, sum);
|
||||
amixer_commit_write(amixer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct amixer_rsc_ops amixer_ops = {
|
||||
.set_input = amixer_set_input,
|
||||
.set_invalid_squash = amixer_set_invalid_squash,
|
||||
.set_scale = amixer_set_y,
|
||||
.set_sum = amixer_set_sum,
|
||||
.commit_write = amixer_commit_write,
|
||||
.commit_raw_write = amixer_commit_raw_write,
|
||||
.setup = amixer_setup,
|
||||
.get_scale = amixer_get_y,
|
||||
};
|
||||
|
||||
static int amixer_rsc_init(struct amixer *amixer,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&amixer->rsc, amixer->idx[0],
|
||||
AMIXER, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set amixer specific operations */
|
||||
amixer->rsc.ops = &amixer_basic_rsc_ops;
|
||||
amixer->ops = &amixer_ops;
|
||||
amixer->input = NULL;
|
||||
amixer->sum = NULL;
|
||||
|
||||
amixer_setup(amixer, NULL, 0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amixer_rsc_uninit(struct amixer *amixer)
|
||||
{
|
||||
amixer_setup(amixer, NULL, 0, NULL);
|
||||
rsc_uninit(&amixer->rsc);
|
||||
amixer->ops = NULL;
|
||||
amixer->input = NULL;
|
||||
amixer->sum = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_amixer_rsc(struct amixer_mgr *mgr,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer **ramixer)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct amixer *amixer;
|
||||
unsigned long flags;
|
||||
|
||||
*ramixer = NULL;
|
||||
|
||||
/* Allocate mem for amixer resource */
|
||||
amixer = kzalloc(sizeof(*amixer), GFP_KERNEL);
|
||||
if (NULL == amixer) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient
|
||||
* amixer resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
amixer->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = amixer_rsc_init(amixer, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*ramixer = amixer;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(amixer);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < amixer->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
amixer_rsc_uninit(amixer);
|
||||
kfree(amixer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
|
||||
{
|
||||
int err;
|
||||
struct amixer_mgr *amixer_mgr;
|
||||
|
||||
*ramixer_mgr = NULL;
|
||||
amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
|
||||
if (NULL == amixer_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
spin_lock_init(&amixer_mgr->mgr_lock);
|
||||
|
||||
amixer_mgr->get_amixer = get_amixer_rsc;
|
||||
amixer_mgr->put_amixer = put_amixer_rsc;
|
||||
|
||||
*ramixer_mgr = amixer_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(amixer_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&amixer_mgr->mgr);
|
||||
kfree(amixer_mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SUM resource management */
|
||||
|
||||
static int sum_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int sum_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int sum_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int sum_output_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (sum_index(rsc) << 4) + 0xc;
|
||||
}
|
||||
|
||||
static struct rsc_ops sum_basic_rsc_ops = {
|
||||
.master = sum_master,
|
||||
.next_conj = sum_next_conj,
|
||||
.index = sum_index,
|
||||
.output_slot = sum_output_slot,
|
||||
};
|
||||
|
||||
static int sum_rsc_init(struct sum *sum,
|
||||
const struct sum_desc *desc,
|
||||
struct sum_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sum->rsc.ops = &sum_basic_rsc_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sum_rsc_uninit(struct sum *sum)
|
||||
{
|
||||
rsc_uninit(&sum->rsc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_sum_rsc(struct sum_mgr *mgr,
|
||||
const struct sum_desc *desc,
|
||||
struct sum **rsum)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct sum *sum;
|
||||
unsigned long flags;
|
||||
|
||||
*rsum = NULL;
|
||||
|
||||
/* Allocate mem for sum resource */
|
||||
sum = kzalloc(sizeof(*sum), GFP_KERNEL);
|
||||
if (NULL == sum) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient sum resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
sum->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = sum_rsc_init(sum, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rsum = sum;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(sum);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < sum->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
sum_rsc_uninit(sum);
|
||||
kfree(sum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
|
||||
{
|
||||
int err;
|
||||
struct sum_mgr *sum_mgr;
|
||||
|
||||
*rsum_mgr = NULL;
|
||||
sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
|
||||
if (NULL == sum_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
spin_lock_init(&sum_mgr->mgr_lock);
|
||||
|
||||
sum_mgr->get_sum = get_sum_rsc;
|
||||
sum_mgr->put_sum = put_sum_rsc;
|
||||
|
||||
*rsum_mgr = sum_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(sum_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int sum_mgr_destroy(struct sum_mgr *sum_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&sum_mgr->mgr);
|
||||
kfree(sum_mgr);
|
||||
return 0;
|
||||
}
|
||||
|
96
sound/pci/ctxfi/ctamixer.h
Normal file
96
sound/pci/ctxfi/ctamixer.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctamixer.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the Audio Mixer
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 21 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTAMIXER_H
|
||||
#define CTAMIXER_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* Define the descriptor of a summation node resource */
|
||||
struct sum {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
unsigned char idx[8];
|
||||
};
|
||||
|
||||
/* Define sum resource request description info */
|
||||
struct sum_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct sum_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request one sum resource */
|
||||
int (*get_sum)(struct sum_mgr *mgr,
|
||||
const struct sum_desc *desc, struct sum **rsum);
|
||||
/* return one sum resource */
|
||||
int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of daio resource manager */
|
||||
int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
|
||||
int sum_mgr_destroy(struct sum_mgr *sum_mgr);
|
||||
|
||||
/* Define the descriptor of a amixer resource */
|
||||
struct amixer_rsc_ops;
|
||||
|
||||
struct amixer {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
unsigned char idx[8];
|
||||
struct rsc *input; /* pointer to a resource acting as source */
|
||||
struct sum *sum; /* Put amixer output to this summation node */
|
||||
struct amixer_rsc_ops *ops; /* AMixer specific operations */
|
||||
};
|
||||
|
||||
struct amixer_rsc_ops {
|
||||
int (*set_input)(struct amixer *amixer, struct rsc *rsc);
|
||||
int (*set_scale)(struct amixer *amixer, unsigned int scale);
|
||||
int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
|
||||
int (*set_sum)(struct amixer *amixer, struct sum *sum);
|
||||
int (*commit_write)(struct amixer *amixer);
|
||||
/* Only for interleaved recording */
|
||||
int (*commit_raw_write)(struct amixer *amixer);
|
||||
int (*setup)(struct amixer *amixer, struct rsc *input,
|
||||
unsigned int scale, struct sum *sum);
|
||||
int (*get_scale)(struct amixer *amixer);
|
||||
};
|
||||
|
||||
/* Define amixer resource request description info */
|
||||
struct amixer_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct amixer_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request one amixer resource */
|
||||
int (*get_amixer)(struct amixer_mgr *mgr,
|
||||
const struct amixer_desc *desc,
|
||||
struct amixer **ramixer);
|
||||
/* return one amixer resource */
|
||||
int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of amixer resource manager */
|
||||
int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
|
||||
int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
|
||||
|
||||
#endif /* CTAMIXER_H */
|
1619
sound/pci/ctxfi/ctatc.c
Normal file
1619
sound/pci/ctxfi/ctatc.c
Normal file
File diff suppressed because it is too large
Load diff
147
sound/pci/ctxfi/ctatc.h
Normal file
147
sound/pci/ctxfi/ctatc.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctatc.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the device resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTATC_H
|
||||
#define CTATC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/timer.h>
|
||||
#include <sound/core.h>
|
||||
|
||||
#include "ctvmem.h"
|
||||
#include "ctresource.h"
|
||||
|
||||
enum CTALSADEVS { /* Types of alsa devices */
|
||||
FRONT,
|
||||
SURROUND,
|
||||
CLFE,
|
||||
SIDE,
|
||||
IEC958,
|
||||
MIXER,
|
||||
NUM_CTALSADEVS /* This should always be the last */
|
||||
};
|
||||
|
||||
struct ct_atc_chip_sub_details {
|
||||
u16 subsys;
|
||||
const char *nm_model;
|
||||
};
|
||||
|
||||
struct ct_atc_chip_details {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
const struct ct_atc_chip_sub_details *sub_details;
|
||||
const char *nm_card;
|
||||
};
|
||||
|
||||
struct ct_atc;
|
||||
struct ct_timer;
|
||||
struct ct_timer_instance;
|
||||
|
||||
/* alsa pcm stream descriptor */
|
||||
struct ct_atc_pcm {
|
||||
struct snd_pcm_substream *substream;
|
||||
void (*interrupt)(struct ct_atc_pcm *apcm);
|
||||
struct ct_timer_instance *timer;
|
||||
unsigned int started:1;
|
||||
|
||||
/* Only mono and interleaved modes are supported now. */
|
||||
struct ct_vm_block *vm_block;
|
||||
void *src; /* SRC for interacting with host memory */
|
||||
void **srccs; /* SRCs for sample rate conversion */
|
||||
void **srcimps; /* SRC Input Mappers */
|
||||
void **amixers; /* AMIXERs for routing converted data */
|
||||
void *mono; /* A SUM resource for mixing chs to one */
|
||||
unsigned char n_srcc; /* Number of converting SRCs */
|
||||
unsigned char n_srcimp; /* Number of SRC Input Mappers */
|
||||
unsigned char n_amixer; /* Number of AMIXERs */
|
||||
};
|
||||
|
||||
/* Chip resource management object */
|
||||
struct ct_atc {
|
||||
struct pci_dev *pci;
|
||||
struct snd_card *card;
|
||||
unsigned int rsr; /* reference sample rate in Hz */
|
||||
unsigned int msr; /* master sample rate in rsr */
|
||||
unsigned int pll_rate; /* current rate of Phase Lock Loop */
|
||||
|
||||
int chip_type;
|
||||
int model;
|
||||
const char *chip_name;
|
||||
const char *model_name;
|
||||
|
||||
struct ct_vm *vm; /* device virtual memory manager for this card */
|
||||
int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index);
|
||||
|
||||
spinlock_t atc_lock;
|
||||
|
||||
int (*pcm_playback_prepare)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_playback_position)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*spdif_passthru_playback_prepare)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
|
||||
int (*pcm_capture_position)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*pcm_release_resources)(struct ct_atc *atc,
|
||||
struct ct_atc_pcm *apcm);
|
||||
int (*select_line_in)(struct ct_atc *atc);
|
||||
int (*select_mic_in)(struct ct_atc *atc);
|
||||
int (*select_digit_io)(struct ct_atc *atc);
|
||||
int (*line_front_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
|
||||
int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
|
||||
int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
|
||||
int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
|
||||
int (*have_digit_io_switch)(struct ct_atc *atc);
|
||||
|
||||
/* Don't touch! Used for internal object. */
|
||||
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
|
||||
void *mixer; /* internal mixer object */
|
||||
void *hw; /* chip specific hardware access object */
|
||||
void **daios; /* digital audio io resources */
|
||||
void **pcm; /* SUMs for collecting all pcm stream */
|
||||
void **srcs; /* Sample Rate Converters for input signal */
|
||||
void **srcimps; /* input mappers for SRCs */
|
||||
unsigned char n_daio;
|
||||
unsigned char n_src;
|
||||
unsigned char n_srcimp;
|
||||
unsigned char n_pcm;
|
||||
|
||||
struct ct_timer *timer;
|
||||
};
|
||||
|
||||
|
||||
int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
|
||||
unsigned int rsr, unsigned int msr, int chip_type,
|
||||
struct ct_atc **ratc);
|
||||
int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
|
||||
|
||||
#endif /* CTATC_H */
|
769
sound/pci/ctxfi/ctdaio.c
Normal file
769
sound/pci/ctxfi/ctdaio.c
Normal file
|
@ -0,0 +1,769 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctdaio.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of Digital Audio Input Output
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctdaio.h"
|
||||
#include "cthardware.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define DAIO_RESOURCE_NUM NUM_DAIOTYP
|
||||
#define DAIO_OUT_MAX SPDIFOO
|
||||
|
||||
union daio_usage {
|
||||
struct {
|
||||
unsigned short lineo1:1;
|
||||
unsigned short lineo2:1;
|
||||
unsigned short lineo3:1;
|
||||
unsigned short lineo4:1;
|
||||
unsigned short spdifoo:1;
|
||||
unsigned short lineim:1;
|
||||
unsigned short spdifio:1;
|
||||
unsigned short spdifi1:1;
|
||||
} bf;
|
||||
unsigned short data;
|
||||
};
|
||||
|
||||
struct daio_rsc_idx {
|
||||
unsigned short left;
|
||||
unsigned short right;
|
||||
};
|
||||
|
||||
struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
|
||||
[LINEO1] = {.left = 0x00, .right = 0x01},
|
||||
[LINEO2] = {.left = 0x18, .right = 0x19},
|
||||
[LINEO3] = {.left = 0x08, .right = 0x09},
|
||||
[LINEO4] = {.left = 0x10, .right = 0x11},
|
||||
[LINEIM] = {.left = 0x1b5, .right = 0x1bd},
|
||||
[SPDIFOO] = {.left = 0x20, .right = 0x21},
|
||||
[SPDIFIO] = {.left = 0x15, .right = 0x1d},
|
||||
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
|
||||
};
|
||||
|
||||
struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
|
||||
[LINEO1] = {.left = 0x40, .right = 0x41},
|
||||
[LINEO2] = {.left = 0x70, .right = 0x71},
|
||||
[LINEO3] = {.left = 0x50, .right = 0x51},
|
||||
[LINEO4] = {.left = 0x60, .right = 0x61},
|
||||
[LINEIM] = {.left = 0x45, .right = 0xc5},
|
||||
[SPDIFOO] = {.left = 0x00, .right = 0x01},
|
||||
[SPDIFIO] = {.left = 0x05, .right = 0x85},
|
||||
};
|
||||
|
||||
static int daio_master(struct rsc *rsc)
|
||||
{
|
||||
/* Actually, this is not the resource index of DAIO.
|
||||
* For DAO, it is the input mapper index. And, for DAI,
|
||||
* it is the output time-slot index. */
|
||||
return rsc->conj = rsc->idx;
|
||||
}
|
||||
|
||||
static int daio_index(const struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int daio_out_next_conj(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 2;
|
||||
}
|
||||
|
||||
static int daio_in_next_conj_20k1(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 0x200;
|
||||
}
|
||||
|
||||
static int daio_in_next_conj_20k2(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj += 0x100;
|
||||
}
|
||||
|
||||
static struct rsc_ops daio_out_rsc_ops = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_out_next_conj,
|
||||
.index = daio_index,
|
||||
.output_slot = NULL,
|
||||
};
|
||||
|
||||
static struct rsc_ops daio_in_rsc_ops_20k1 = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_in_next_conj_20k1,
|
||||
.index = NULL,
|
||||
.output_slot = daio_index,
|
||||
};
|
||||
|
||||
static struct rsc_ops daio_in_rsc_ops_20k2 = {
|
||||
.master = daio_master,
|
||||
.next_conj = daio_in_next_conj_20k2,
|
||||
.index = NULL,
|
||||
.output_slot = daio_index,
|
||||
};
|
||||
|
||||
static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
|
||||
{
|
||||
switch (hw->chip_type) {
|
||||
case ATC20K1:
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case SPDIFI1: return 1;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
case LINEO4: return 6;
|
||||
case LINEIM: return 7;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
case ATC20K2:
|
||||
switch (type) {
|
||||
case SPDIFOO: return 0;
|
||||
case SPDIFIO: return 0;
|
||||
case LINEO1: return 4;
|
||||
case LINEO2: return 7;
|
||||
case LINEO3: return 5;
|
||||
case LINEO4: return 6;
|
||||
case LINEIM: return 4;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
|
||||
|
||||
static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_commit_write(struct dao *dao)
|
||||
{
|
||||
((struct hw *)dao->hw)->dao_commit_write(dao->hw,
|
||||
daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_set_left_input(struct dao *dao, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
input->ops->master(input);
|
||||
daio->rscl.ops->master(&daio->rscl);
|
||||
for (i = 0; i < daio->rscl.msr; i++, entry++) {
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl);
|
||||
dao->mgr->imap_add(dao->mgr, entry);
|
||||
dao->imappers[i] = entry;
|
||||
|
||||
input->ops->next_conj(input);
|
||||
daio->rscl.ops->next_conj(&daio->rscl);
|
||||
}
|
||||
input->ops->master(input);
|
||||
daio->rscl.ops->master(&daio->rscl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_set_right_input(struct dao *dao, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
|
||||
if (NULL == entry)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
input->ops->master(input);
|
||||
daio->rscr.ops->master(&daio->rscr);
|
||||
for (i = 0; i < daio->rscr.msr; i++, entry++) {
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr);
|
||||
dao->mgr->imap_add(dao->mgr, entry);
|
||||
dao->imappers[daio->rscl.msr + i] = entry;
|
||||
|
||||
input->ops->next_conj(input);
|
||||
daio->rscr.ops->next_conj(&daio->rscr);
|
||||
}
|
||||
input->ops->master(input);
|
||||
daio->rscr.ops->master(&daio->rscr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_clear_left_input(struct dao *dao)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[0])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[0];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscl.msr; i++) {
|
||||
entry = dao->imappers[i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
dao->imappers[i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[0]);
|
||||
dao->imappers[0] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_clear_right_input(struct dao *dao)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (NULL == dao->imappers[daio->rscl.msr])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[daio->rscl.msr];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscr.msr; i++) {
|
||||
entry = dao->imappers[daio->rscl.msr + i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
dao->imappers[daio->rscl.msr + i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[daio->rscl.msr]);
|
||||
dao->imappers[daio->rscl.msr] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dao_rsc_ops dao_ops = {
|
||||
.set_spos = dao_spdif_set_spos,
|
||||
.commit_write = dao_commit_write,
|
||||
.get_spos = dao_spdif_get_spos,
|
||||
.reinit = dao_rsc_reinit,
|
||||
.set_left_input = dao_set_left_input,
|
||||
.set_right_input = dao_set_right_input,
|
||||
.clear_left_input = dao_clear_left_input,
|
||||
.clear_right_input = dao_clear_right_input,
|
||||
};
|
||||
|
||||
static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
|
||||
{
|
||||
src->ops->master(src);
|
||||
((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
|
||||
src->ops->index(src));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
|
||||
{
|
||||
src->ops->master(src);
|
||||
((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
|
||||
src->ops->index(src));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
|
||||
{
|
||||
unsigned int rsr;
|
||||
|
||||
for (rsr = 0; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_enb_src(struct dai *dai, unsigned int enb)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_commit_write(struct dai *dai)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_commit_write(dai->hw,
|
||||
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dai_rsc_ops dai_ops = {
|
||||
.set_srt_srcl = dai_set_srt_srcl,
|
||||
.set_srt_srcr = dai_set_srt_srcr,
|
||||
.set_srt_msr = dai_set_srt_msr,
|
||||
.set_enb_src = dai_set_enb_src,
|
||||
.set_enb_srt = dai_set_enb_srt,
|
||||
.commit_write = dai_commit_write,
|
||||
};
|
||||
|
||||
static int daio_rsc_init(struct daio *daio,
|
||||
const struct daio_desc *desc,
|
||||
void *hw)
|
||||
{
|
||||
int err;
|
||||
unsigned int idx_l, idx_r;
|
||||
|
||||
switch (((struct hw *)hw)->chip_type) {
|
||||
case ATC20K1:
|
||||
idx_l = idx_20k1[desc->type].left;
|
||||
idx_r = idx_20k1[desc->type].right;
|
||||
break;
|
||||
case ATC20K2:
|
||||
idx_l = idx_20k2[desc->type].left;
|
||||
idx_r = idx_20k2[desc->type].right;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
/* Set daio->rscl/r->ops to daio specific ones */
|
||||
if (desc->type <= DAIO_OUT_MAX) {
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
|
||||
} else {
|
||||
switch (((struct hw *)hw)->chip_type) {
|
||||
case ATC20K1:
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
|
||||
break;
|
||||
case ATC20K2:
|
||||
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
daio->type = desc->type;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
rsc_uninit(&daio->rscl);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_rsc_uninit(struct daio *daio)
|
||||
{
|
||||
rsc_uninit(&daio->rscl);
|
||||
rsc_uninit(&daio->rscr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_rsc_init(struct dao *dao,
|
||||
const struct daio_desc *desc,
|
||||
struct daio_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
unsigned int conf;
|
||||
int err;
|
||||
|
||||
err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
|
||||
if (NULL == dao->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
dao->ops = &dao_ops;
|
||||
dao->mgr = mgr;
|
||||
dao->hw = hw;
|
||||
err = hw->dao_get_ctrl_blk(&dao->ctrl_blk);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw));
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
conf = (desc->msr & 0x7) | (desc->passthru << 3);
|
||||
hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw), conf);
|
||||
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(dao->daio.type, hw));
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
kfree(dao->imappers);
|
||||
dao->imappers = NULL;
|
||||
error1:
|
||||
daio_rsc_uninit(&dao->daio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dao_rsc_uninit(struct dao *dao)
|
||||
{
|
||||
if (NULL != dao->imappers) {
|
||||
if (NULL != dao->imappers[0])
|
||||
dao_clear_left_input(dao);
|
||||
|
||||
if (NULL != dao->imappers[dao->daio.rscl.msr])
|
||||
dao_clear_right_input(dao);
|
||||
|
||||
kfree(dao->imappers);
|
||||
dao->imappers = NULL;
|
||||
}
|
||||
((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
|
||||
dao->hw = dao->ctrl_blk = NULL;
|
||||
daio_rsc_uninit(&dao->daio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
|
||||
{
|
||||
struct daio_mgr *mgr = dao->mgr;
|
||||
struct daio_desc dsc = {0};
|
||||
|
||||
dsc.type = dao->daio.type;
|
||||
dsc.msr = desc->msr;
|
||||
dsc.passthru = desc->passthru;
|
||||
dao_rsc_uninit(dao);
|
||||
return dao_rsc_init(dao, &dsc, mgr);
|
||||
}
|
||||
|
||||
static int dai_rsc_init(struct dai *dai,
|
||||
const struct daio_desc *desc,
|
||||
struct daio_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
unsigned int rsr, msr;
|
||||
|
||||
err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dai->ops = &dai_ops;
|
||||
dai->hw = mgr->mgr.hw;
|
||||
err = hw->dai_get_ctrl_blk(&dai->ctrl_blk);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
|
||||
hw->dai_srt_set_drat(dai->ctrl_blk, 0);
|
||||
/* default to disabling control of a SRC */
|
||||
hw->dai_srt_set_ec(dai->ctrl_blk, 0);
|
||||
hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
|
||||
hw->dai_commit_write(hw,
|
||||
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
daio_rsc_uninit(&dai->daio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dai_rsc_uninit(struct dai *dai)
|
||||
{
|
||||
((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
|
||||
dai->hw = dai->ctrl_blk = NULL;
|
||||
daio_rsc_uninit(&dai->daio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
|
||||
{
|
||||
if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
|
||||
return -ENOENT;
|
||||
|
||||
((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
|
||||
{
|
||||
((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_daio_rsc(struct daio_mgr *mgr,
|
||||
const struct daio_desc *desc,
|
||||
struct daio **rdaio)
|
||||
{
|
||||
int err;
|
||||
struct dai *dai = NULL;
|
||||
struct dao *dao = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
*rdaio = NULL;
|
||||
|
||||
/* Check whether there are sufficient daio resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Can't meet DAIO resource request!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate mem for daio resource */
|
||||
if (desc->type <= DAIO_OUT_MAX) {
|
||||
dao = kzalloc(sizeof(*dao), GFP_KERNEL);
|
||||
if (NULL == dao) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
err = dao_rsc_init(dao, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rdaio = &dao->daio;
|
||||
} else {
|
||||
dai = kzalloc(sizeof(*dai), GFP_KERNEL);
|
||||
if (NULL == dai) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
err = dai_rsc_init(dai, desc, mgr);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
*rdaio = &dai->daio;
|
||||
}
|
||||
|
||||
mgr->daio_enable(mgr, *rdaio);
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (NULL != dao)
|
||||
kfree(dao);
|
||||
else if (NULL != dai)
|
||||
kfree(dai);
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
daio_mgr_put_rsc(&mgr->mgr, desc->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
mgr->daio_disable(mgr, daio);
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
daio_mgr_put_rsc(&mgr->mgr, daio->type);
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
|
||||
if (daio->type <= DAIO_OUT_MAX) {
|
||||
dao_rsc_uninit(container_of(daio, struct dao, daio));
|
||||
kfree(container_of(daio, struct dao, daio));
|
||||
} else {
|
||||
dai_rsc_uninit(container_of(daio, struct dai, daio));
|
||||
kfree(container_of(daio, struct dai, daio));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
if (DAIO_OUT_MAX >= daio->type) {
|
||||
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
} else {
|
||||
hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
if (DAIO_OUT_MAX >= daio->type) {
|
||||
hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
} else {
|
||||
hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
|
||||
daio_device_index(daio->type, hw));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_map_op(void *data, struct imapper *entry)
|
||||
{
|
||||
struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr;
|
||||
struct hw *hw = mgr->hw;
|
||||
|
||||
hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
|
||||
hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
|
||||
hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
|
||||
hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
if ((0 == entry->addr) && (mgr->init_imap_added)) {
|
||||
input_mapper_delete(&mgr->imappers, mgr->init_imap,
|
||||
daio_map_op, mgr);
|
||||
mgr->init_imap_added = 0;
|
||||
}
|
||||
err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
|
||||
if (list_empty(&mgr->imappers)) {
|
||||
input_mapper_add(&mgr->imappers, mgr->init_imap,
|
||||
daio_map_op, mgr);
|
||||
mgr->init_imap_added = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daio_mgr_commit_write(struct daio_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
|
||||
{
|
||||
int err, i;
|
||||
struct daio_mgr *daio_mgr;
|
||||
struct imapper *entry;
|
||||
|
||||
*rdaio_mgr = NULL;
|
||||
daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
|
||||
if (NULL == daio_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&daio_mgr->mgr_lock);
|
||||
spin_lock_init(&daio_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&daio_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
entry->slot = entry->addr = entry->next = entry->user = 0;
|
||||
list_add(&entry->list, &daio_mgr->imappers);
|
||||
daio_mgr->init_imap = entry;
|
||||
daio_mgr->init_imap_added = 1;
|
||||
|
||||
daio_mgr->get_daio = get_daio_rsc;
|
||||
daio_mgr->put_daio = put_daio_rsc;
|
||||
daio_mgr->daio_enable = daio_mgr_enb_daio;
|
||||
daio_mgr->daio_disable = daio_mgr_dsb_daio;
|
||||
daio_mgr->imap_add = daio_imap_add;
|
||||
daio_mgr->imap_delete = daio_imap_delete;
|
||||
daio_mgr->commit_write = daio_mgr_commit_write;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
|
||||
((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
|
||||
}
|
||||
((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
|
||||
|
||||
*rdaio_mgr = daio_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
rsc_mgr_uninit(&daio_mgr->mgr);
|
||||
error1:
|
||||
kfree(daio_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int daio_mgr_destroy(struct daio_mgr *daio_mgr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* free daio input mapper list */
|
||||
spin_lock_irqsave(&daio_mgr->imap_lock, flags);
|
||||
free_input_mapper_list(&daio_mgr->imappers);
|
||||
spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
|
||||
|
||||
rsc_mgr_uninit(&daio_mgr->mgr);
|
||||
kfree(daio_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
122
sound/pci/ctxfi/ctdaio.h
Normal file
122
sound/pci/ctxfi/ctdaio.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctdaio.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of Digital Audio Input Output
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTDAIO_H
|
||||
#define CTDAIO_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* Define the descriptor of a daio resource */
|
||||
enum DAIOTYP {
|
||||
LINEO1,
|
||||
LINEO2,
|
||||
LINEO3,
|
||||
LINEO4,
|
||||
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
|
||||
LINEIM,
|
||||
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
|
||||
SPDIFI1, /* S/PDIF In on internal Drive Bay */
|
||||
NUM_DAIOTYP
|
||||
};
|
||||
|
||||
struct dao_rsc_ops;
|
||||
struct dai_rsc_ops;
|
||||
struct daio_mgr;
|
||||
|
||||
struct daio {
|
||||
struct rsc rscl; /* Basic resource info for left TX/RX */
|
||||
struct rsc rscr; /* Basic resource info for right TX/RX */
|
||||
enum DAIOTYP type;
|
||||
};
|
||||
|
||||
struct dao {
|
||||
struct daio daio;
|
||||
struct dao_rsc_ops *ops; /* DAO specific operations */
|
||||
struct imapper **imappers;
|
||||
struct daio_mgr *mgr;
|
||||
void *hw;
|
||||
void *ctrl_blk;
|
||||
};
|
||||
|
||||
struct dai {
|
||||
struct daio daio;
|
||||
struct dai_rsc_ops *ops; /* DAI specific operations */
|
||||
void *hw;
|
||||
void *ctrl_blk;
|
||||
};
|
||||
|
||||
struct dao_desc {
|
||||
unsigned int msr:4;
|
||||
unsigned int passthru:1;
|
||||
};
|
||||
|
||||
struct dao_rsc_ops {
|
||||
int (*set_spos)(struct dao *dao, unsigned int spos);
|
||||
int (*commit_write)(struct dao *dao);
|
||||
int (*get_spos)(struct dao *dao, unsigned int *spos);
|
||||
int (*reinit)(struct dao *dao, const struct dao_desc *desc);
|
||||
int (*set_left_input)(struct dao *dao, struct rsc *input);
|
||||
int (*set_right_input)(struct dao *dao, struct rsc *input);
|
||||
int (*clear_left_input)(struct dao *dao);
|
||||
int (*clear_right_input)(struct dao *dao);
|
||||
};
|
||||
|
||||
struct dai_rsc_ops {
|
||||
int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
|
||||
int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
|
||||
int (*set_srt_msr)(struct dai *dai, unsigned int msr);
|
||||
int (*set_enb_src)(struct dai *dai, unsigned int enb);
|
||||
int (*set_enb_srt)(struct dai *dai, unsigned int enb);
|
||||
int (*commit_write)(struct dai *dai);
|
||||
};
|
||||
|
||||
/* Define daio resource request description info */
|
||||
struct daio_desc {
|
||||
unsigned int type:4;
|
||||
unsigned int msr:4;
|
||||
unsigned int passthru:1;
|
||||
};
|
||||
|
||||
struct daio_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
spinlock_t imap_lock;
|
||||
struct list_head imappers;
|
||||
struct imapper *init_imap;
|
||||
unsigned int init_imap_added;
|
||||
|
||||
/* request one daio resource */
|
||||
int (*get_daio)(struct daio_mgr *mgr,
|
||||
const struct daio_desc *desc, struct daio **rdaio);
|
||||
/* return one daio resource */
|
||||
int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
|
||||
int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
|
||||
int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
|
||||
int (*commit_write)(struct daio_mgr *mgr);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of daio resource manager */
|
||||
int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
|
||||
int daio_mgr_destroy(struct daio_mgr *daio_mgr);
|
||||
|
||||
#endif /* CTDAIO_H */
|
91
sound/pci/ctxfi/cthardware.c
Normal file
91
sound/pci/ctxfi/cthardware.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File cthardware.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Jun 26 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cthardware.h"
|
||||
#include "cthw20k1.h"
|
||||
#include "cthw20k2.h"
|
||||
#include <linux/bug.h>
|
||||
|
||||
int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
|
||||
enum CTCARDS model, struct hw **rhw)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (chip_type) {
|
||||
case ATC20K1:
|
||||
err = create_20k1_hw_obj(rhw);
|
||||
break;
|
||||
case ATC20K2:
|
||||
err = create_20k2_hw_obj(rhw);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
(*rhw)->pci = pci;
|
||||
(*rhw)->chip_type = chip_type;
|
||||
(*rhw)->model = model;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int destroy_hw_obj(struct hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (hw->pci->device) {
|
||||
case 0x0005: /* 20k1 device */
|
||||
err = destroy_20k1_hw_obj(hw);
|
||||
break;
|
||||
case 0x000B: /* 20k2 device */
|
||||
err = destroy_20k2_hw_obj(hw);
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
unsigned int get_field(unsigned int data, unsigned int field)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(!field);
|
||||
/* @field should always be greater than 0 */
|
||||
for (i = 0; !(field & (1 << i)); )
|
||||
i++;
|
||||
|
||||
return (data & field) >> i;
|
||||
}
|
||||
|
||||
void set_field(unsigned int *data, unsigned int field, unsigned int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(!field);
|
||||
/* @field should always be greater than 0 */
|
||||
for (i = 0; !(field & (1 << i)); )
|
||||
i++;
|
||||
|
||||
*data = (*data & (~field)) | ((value << i) & field);
|
||||
}
|
||||
|
196
sound/pci/ctxfi/cthardware.h
Normal file
196
sound/pci/ctxfi/cthardware.h
Normal file
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File cthardware.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHARDWARE_H
|
||||
#define CTHARDWARE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
enum CHIPTYP {
|
||||
ATC20K1,
|
||||
ATC20K2,
|
||||
ATCNONE
|
||||
};
|
||||
|
||||
enum CTCARDS {
|
||||
/* 20k1 models */
|
||||
CTSB055X,
|
||||
CTSB073X,
|
||||
CTUAA,
|
||||
CT20K1_UNKNOWN,
|
||||
/* 20k2 models */
|
||||
CTSB0760,
|
||||
CTHENDRIX,
|
||||
CTSB0880,
|
||||
NUM_CTCARDS /* This should always be the last */
|
||||
};
|
||||
|
||||
/* Type of input source for ADC */
|
||||
enum ADCSRC{
|
||||
ADC_MICIN,
|
||||
ADC_LINEIN,
|
||||
ADC_VIDEO,
|
||||
ADC_AUX,
|
||||
ADC_NONE /* Switch to digital input */
|
||||
};
|
||||
|
||||
struct card_conf {
|
||||
/* device virtual mem page table page physical addr
|
||||
* (supporting one page table page now) */
|
||||
unsigned long vm_pgt_phys;
|
||||
unsigned int rsr; /* reference sample rate in Hzs*/
|
||||
unsigned int msr; /* master sample rate in rsrs */
|
||||
};
|
||||
|
||||
struct hw {
|
||||
int (*card_init)(struct hw *hw, struct card_conf *info);
|
||||
int (*card_stop)(struct hw *hw);
|
||||
int (*pll_init)(struct hw *hw, unsigned int rsr);
|
||||
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
|
||||
int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
|
||||
int (*have_digit_io_switch)(struct hw *hw);
|
||||
|
||||
/* SRC operations */
|
||||
int (*src_rsc_get_ctrl_blk)(void **rblk);
|
||||
int (*src_rsc_put_ctrl_blk)(void *blk);
|
||||
int (*src_set_state)(void *blk, unsigned int state);
|
||||
int (*src_set_bm)(void *blk, unsigned int bm);
|
||||
int (*src_set_rsr)(void *blk, unsigned int rsr);
|
||||
int (*src_set_sf)(void *blk, unsigned int sf);
|
||||
int (*src_set_wr)(void *blk, unsigned int wr);
|
||||
int (*src_set_pm)(void *blk, unsigned int pm);
|
||||
int (*src_set_rom)(void *blk, unsigned int rom);
|
||||
int (*src_set_vo)(void *blk, unsigned int vo);
|
||||
int (*src_set_st)(void *blk, unsigned int st);
|
||||
int (*src_set_ie)(void *blk, unsigned int ie);
|
||||
int (*src_set_ilsz)(void *blk, unsigned int ilsz);
|
||||
int (*src_set_bp)(void *blk, unsigned int bp);
|
||||
int (*src_set_cisz)(void *blk, unsigned int cisz);
|
||||
int (*src_set_ca)(void *blk, unsigned int ca);
|
||||
int (*src_set_sa)(void *blk, unsigned int sa);
|
||||
int (*src_set_la)(void *blk, unsigned int la);
|
||||
int (*src_set_pitch)(void *blk, unsigned int pitch);
|
||||
int (*src_set_clear_zbufs)(void *blk, unsigned int clear);
|
||||
int (*src_set_dirty)(void *blk, unsigned int flags);
|
||||
int (*src_set_dirty_all)(void *blk);
|
||||
int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk);
|
||||
unsigned int (*src_get_dirty)(void *blk);
|
||||
unsigned int (*src_dirty_conj_mask)(void);
|
||||
int (*src_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*src_mgr_put_ctrl_blk)(void *blk);
|
||||
/* syncly enable src @idx */
|
||||
int (*src_mgr_enbs_src)(void *blk, unsigned int idx);
|
||||
/* enable src @idx */
|
||||
int (*src_mgr_enb_src)(void *blk, unsigned int idx);
|
||||
/* disable src @idx */
|
||||
int (*src_mgr_dsb_src)(void *blk, unsigned int idx);
|
||||
int (*src_mgr_commit_write)(struct hw *hw, void *blk);
|
||||
|
||||
/* SRC Input Mapper operations */
|
||||
int (*srcimp_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*srcimp_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot);
|
||||
int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user);
|
||||
int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next);
|
||||
int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr);
|
||||
int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk);
|
||||
|
||||
/* AMIXER operations */
|
||||
int (*amixer_rsc_get_ctrl_blk)(void **rblk);
|
||||
int (*amixer_rsc_put_ctrl_blk)(void *blk);
|
||||
int (*amixer_mgr_get_ctrl_blk)(void **rblk);
|
||||
int (*amixer_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*amixer_set_mode)(void *blk, unsigned int mode);
|
||||
int (*amixer_set_iv)(void *blk, unsigned int iv);
|
||||
int (*amixer_set_x)(void *blk, unsigned int x);
|
||||
int (*amixer_set_y)(void *blk, unsigned int y);
|
||||
int (*amixer_set_sadr)(void *blk, unsigned int sadr);
|
||||
int (*amixer_set_se)(void *blk, unsigned int se);
|
||||
int (*amixer_set_dirty)(void *blk, unsigned int flags);
|
||||
int (*amixer_set_dirty_all)(void *blk);
|
||||
int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*amixer_get_y)(void *blk);
|
||||
unsigned int (*amixer_get_dirty)(void *blk);
|
||||
|
||||
/* DAIO operations */
|
||||
int (*dai_get_ctrl_blk)(void **rblk);
|
||||
int (*dai_put_ctrl_blk)(void *blk);
|
||||
int (*dai_srt_set_srco)(void *blk, unsigned int src);
|
||||
int (*dai_srt_set_srcm)(void *blk, unsigned int src);
|
||||
int (*dai_srt_set_rsr)(void *blk, unsigned int rsr);
|
||||
int (*dai_srt_set_drat)(void *blk, unsigned int drat);
|
||||
int (*dai_srt_set_ec)(void *blk, unsigned int ec);
|
||||
int (*dai_srt_set_et)(void *blk, unsigned int et);
|
||||
int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*dao_get_ctrl_blk)(void **rblk);
|
||||
int (*dao_put_ctrl_blk)(void *blk);
|
||||
int (*dao_set_spos)(void *blk, unsigned int spos);
|
||||
int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk);
|
||||
int (*dao_get_spos)(void *blk, unsigned int *spos);
|
||||
|
||||
int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk);
|
||||
int (*daio_mgr_put_ctrl_blk)(void *blk);
|
||||
int (*daio_mgr_enb_dai)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
|
||||
int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
|
||||
unsigned int conf);
|
||||
int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
|
||||
int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
|
||||
int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr);
|
||||
int (*daio_mgr_commit_write)(struct hw *hw, void *blk);
|
||||
|
||||
int (*set_timer_irq)(struct hw *hw, int enable);
|
||||
int (*set_timer_tick)(struct hw *hw, unsigned int tick);
|
||||
unsigned int (*get_wc)(struct hw *hw);
|
||||
|
||||
void (*irq_callback)(void *data, unsigned int bit);
|
||||
void *irq_callback_data;
|
||||
|
||||
struct pci_dev *pci; /* the pci kernel structure of this card */
|
||||
int irq;
|
||||
unsigned long io_base;
|
||||
unsigned long mem_base;
|
||||
|
||||
enum CHIPTYP chip_type;
|
||||
enum CTCARDS model;
|
||||
};
|
||||
|
||||
int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
|
||||
enum CTCARDS model, struct hw **rhw);
|
||||
int destroy_hw_obj(struct hw *hw);
|
||||
|
||||
unsigned int get_field(unsigned int data, unsigned int field);
|
||||
void set_field(unsigned int *data, unsigned int field, unsigned int value);
|
||||
|
||||
/* IRQ bits */
|
||||
#define PLL_INT (1 << 10) /* PLL input-clock out-of-range */
|
||||
#define FI_INT (1 << 9) /* forced interrupt */
|
||||
#define IT_INT (1 << 8) /* timer interrupt */
|
||||
#define PCI_INT (1 << 7) /* PCI bus error pending */
|
||||
#define URT_INT (1 << 6) /* UART Tx/Rx */
|
||||
#define GPI_INT (1 << 5) /* GPI pin */
|
||||
#define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */
|
||||
#define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */
|
||||
#define TP_INT (1 << 2) /* transport priority queue */
|
||||
#define DSP_INT (1 << 1) /* DSP */
|
||||
#define SRC_INT (1 << 0) /* SRC channels */
|
||||
|
||||
#endif /* CTHARDWARE_H */
|
2248
sound/pci/ctxfi/cthw20k1.c
Normal file
2248
sound/pci/ctxfi/cthw20k1.c
Normal file
File diff suppressed because it is too large
Load diff
26
sound/pci/ctxfi/cthw20k1.h
Normal file
26
sound/pci/ctxfi/cthw20k1.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File cthw20k1.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHW20K1_H
|
||||
#define CTHW20K1_H
|
||||
|
||||
#include "cthardware.h"
|
||||
|
||||
int create_20k1_hw_obj(struct hw **rhw);
|
||||
int destroy_20k1_hw_obj(struct hw *hw);
|
||||
|
||||
#endif /* CTHW20K1_H */
|
2137
sound/pci/ctxfi/cthw20k2.c
Normal file
2137
sound/pci/ctxfi/cthw20k2.c
Normal file
File diff suppressed because it is too large
Load diff
26
sound/pci/ctxfi/cthw20k2.h
Normal file
26
sound/pci/ctxfi/cthw20k2.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File cthw20k2.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of hardware access methord.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTHW20K2_H
|
||||
#define CTHW20K2_H
|
||||
|
||||
#include "cthardware.h"
|
||||
|
||||
int create_20k2_hw_obj(struct hw **rhw);
|
||||
int destroy_20k2_hw_obj(struct hw *hw);
|
||||
|
||||
#endif /* CTHW20K2_H */
|
112
sound/pci/ctxfi/ctimap.c
Normal file
112
sound/pci/ctxfi/ctimap.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctimap.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of generic input mapper operations
|
||||
* for input mapper management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctimap.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data)
|
||||
{
|
||||
struct list_head *pos, *pre, *head;
|
||||
struct imapper *pre_ent, *pos_ent;
|
||||
|
||||
head = mappers;
|
||||
|
||||
if (list_empty(head)) {
|
||||
entry->next = entry->addr;
|
||||
map_op(data, entry);
|
||||
list_add(&entry->list, head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each(pos, head) {
|
||||
pos_ent = list_entry(pos, struct imapper, list);
|
||||
if (pos_ent->slot > entry->slot) {
|
||||
/* found a position in list */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != head) {
|
||||
pre = pos->prev;
|
||||
if (pre == head)
|
||||
pre = head->prev;
|
||||
|
||||
__list_add(&entry->list, pos->prev, pos);
|
||||
} else {
|
||||
pre = head->prev;
|
||||
pos = head->next;
|
||||
list_add_tail(&entry->list, head);
|
||||
}
|
||||
|
||||
pre_ent = list_entry(pre, struct imapper, list);
|
||||
pos_ent = list_entry(pos, struct imapper, list);
|
||||
|
||||
entry->next = pos_ent->addr;
|
||||
map_op(data, entry);
|
||||
pre_ent->next = entry->addr;
|
||||
map_op(data, pre_ent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data)
|
||||
{
|
||||
struct list_head *next, *pre, *head;
|
||||
struct imapper *pre_ent, *next_ent;
|
||||
|
||||
head = mappers;
|
||||
|
||||
if (list_empty(head))
|
||||
return 0;
|
||||
|
||||
pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
|
||||
next = (entry->list.next == head) ? head->next : entry->list.next;
|
||||
|
||||
if (pre == &entry->list) {
|
||||
/* entry is the only one node in mappers list */
|
||||
entry->next = entry->addr = entry->user = entry->slot = 0;
|
||||
map_op(data, entry);
|
||||
list_del(&entry->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pre_ent = list_entry(pre, struct imapper, list);
|
||||
next_ent = list_entry(next, struct imapper, list);
|
||||
|
||||
pre_ent->next = next_ent->addr;
|
||||
map_op(data, pre_ent);
|
||||
list_del(&entry->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_input_mapper_list(struct list_head *head)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct list_head *pos;
|
||||
|
||||
while (!list_empty(head)) {
|
||||
pos = head->next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct imapper, list);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
40
sound/pci/ctxfi/ctimap.h
Normal file
40
sound/pci/ctxfi/ctimap.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctimap.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of generic input mapper operations
|
||||
* for input mapper management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 23 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTIMAP_H
|
||||
#define CTIMAP_H
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
struct imapper {
|
||||
unsigned short slot; /* the id of the slot containing input data */
|
||||
unsigned short user; /* the id of the user resource consuming data */
|
||||
unsigned short addr; /* the input mapper ram id */
|
||||
unsigned short next; /* the next input mapper ram id */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int input_mapper_add(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data);
|
||||
|
||||
int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
|
||||
int (*map_op)(void *, struct imapper *), void *data);
|
||||
|
||||
void free_input_mapper_list(struct list_head *mappers);
|
||||
|
||||
#endif /* CTIMAP_H */
|
1123
sound/pci/ctxfi/ctmixer.c
Normal file
1123
sound/pci/ctxfi/ctmixer.c
Normal file
File diff suppressed because it is too large
Load diff
67
sound/pci/ctxfi/ctmixer.h
Normal file
67
sound/pci/ctxfi/ctmixer.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctmixer.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the mixer device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTMIXER_H
|
||||
#define CTMIXER_H
|
||||
|
||||
#include "ctatc.h"
|
||||
#include "ctresource.h"
|
||||
|
||||
#define INIT_VOL 0x1c00
|
||||
|
||||
enum MIXER_PORT_T {
|
||||
MIX_WAVE_FRONT,
|
||||
MIX_WAVE_REAR,
|
||||
MIX_WAVE_CENTLFE,
|
||||
MIX_WAVE_SURROUND,
|
||||
MIX_SPDIF_OUT,
|
||||
MIX_PCMO_FRONT,
|
||||
MIX_MIC_IN,
|
||||
MIX_LINE_IN,
|
||||
MIX_SPDIF_IN,
|
||||
MIX_PCMI_FRONT,
|
||||
MIX_PCMI_REAR,
|
||||
MIX_PCMI_CENTLFE,
|
||||
MIX_PCMI_SURROUND,
|
||||
|
||||
NUM_MIX_PORTS
|
||||
};
|
||||
|
||||
/* alsa mixer descriptor */
|
||||
struct ct_mixer {
|
||||
struct ct_atc *atc;
|
||||
|
||||
void **amixers; /* amixer resources for volume control */
|
||||
void **sums; /* sum resources for signal collection */
|
||||
unsigned int switch_state; /* A bit-map to indicate state of switches */
|
||||
|
||||
int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
|
||||
struct rsc **rleft, struct rsc **rright);
|
||||
|
||||
int (*set_input_left)(struct ct_mixer *mixer,
|
||||
enum MIXER_PORT_T type, struct rsc *rsc);
|
||||
int (*set_input_right)(struct ct_mixer *mixer,
|
||||
enum MIXER_PORT_T type, struct rsc *rsc);
|
||||
};
|
||||
|
||||
int ct_alsa_mix_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name);
|
||||
int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
|
||||
int ct_mixer_destroy(struct ct_mixer *mixer);
|
||||
|
||||
#endif /* CTMIXER_H */
|
426
sound/pci/ctxfi/ctpcm.c
Normal file
426
sound/pci/ctxfi/ctpcm.c
Normal file
|
@ -0,0 +1,426 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctpcm.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the pcm device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Apr 2 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctpcm.h"
|
||||
#include "cttimer.h"
|
||||
#include <sound/pcm.h>
|
||||
|
||||
/* Hardware descriptions for playback */
|
||||
static struct snd_pcm_hardware ct_pcm_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.formats = (SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_FLOAT_LE),
|
||||
.rates = (SNDRV_PCM_RATE_CONTINUOUS |
|
||||
SNDRV_PCM_RATE_8000_192000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (64),
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = (SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_32000),
|
||||
.rate_min = 32000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (64),
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
/* Hardware descriptions for capture */
|
||||
static struct snd_pcm_hardware ct_pcm_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = (SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_3LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE |
|
||||
SNDRV_PCM_FMTBIT_FLOAT_LE),
|
||||
.rates = (SNDRV_PCM_RATE_CONTINUOUS |
|
||||
SNDRV_PCM_RATE_8000_96000),
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = (384),
|
||||
.period_bytes_max = (64*1024),
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
|
||||
{
|
||||
struct ct_atc_pcm *apcm = atc_pcm;
|
||||
|
||||
if (NULL == apcm->substream)
|
||||
return;
|
||||
|
||||
snd_pcm_period_elapsed(apcm->substream);
|
||||
}
|
||||
|
||||
static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
|
||||
|
||||
atc->pcm_release_resources(atc, apcm);
|
||||
ct_timer_instance_free(apcm->timer);
|
||||
kfree(apcm);
|
||||
runtime->private_data = NULL;
|
||||
}
|
||||
|
||||
/* pcm playback operations */
|
||||
static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm;
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->substream = substream;
|
||||
apcm->interrupt = ct_atc_pcm_interrupt;
|
||||
runtime->private_data = apcm;
|
||||
runtime->private_free = ct_atc_pcm_free_substream;
|
||||
if (IEC958 == substream->pcm->device) {
|
||||
runtime->hw = ct_spdif_passthru_playback_hw;
|
||||
atc->spdif_out_passthru(atc, 1);
|
||||
} else {
|
||||
runtime->hw = ct_pcm_playback_hw;
|
||||
if (FRONT == substream->pcm->device)
|
||||
runtime->hw.channels_max = 8;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
1024, UINT_MAX);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
|
||||
if (!apcm->timer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
|
||||
/* TODO: Notify mixer inactive. */
|
||||
if (IEC958 == substream->pcm->device)
|
||||
atc->spdif_out_passthru(atc, 0);
|
||||
|
||||
/* The ct_atc_pcm object will be freed by runtime->private_free */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct ct_atc_pcm *apcm = substream->runtime->private_data;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* clear previous resources */
|
||||
atc->pcm_release_resources(atc, apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct ct_atc_pcm *apcm = substream->runtime->private_data;
|
||||
|
||||
/* clear previous resources */
|
||||
atc->pcm_release_resources(atc, apcm);
|
||||
/* Free snd-allocated pages */
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
|
||||
static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int err;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
if (IEC958 == substream->pcm->device)
|
||||
err = atc->spdif_passthru_playback_prepare(atc, apcm);
|
||||
else
|
||||
err = atc->pcm_playback_prepare(atc, apcm);
|
||||
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
atc->pcm_playback_start(atc, apcm);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
atc->pcm_playback_stop(atc, apcm);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long position;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
/* Read out playback position */
|
||||
position = atc->pcm_playback_position(atc, apcm);
|
||||
position = bytes_to_frames(runtime, position);
|
||||
if (position >= runtime->buffer_size)
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
/* pcm capture operations */
|
||||
static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm;
|
||||
int err;
|
||||
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (NULL == apcm)
|
||||
return -ENOMEM;
|
||||
|
||||
apcm->started = 0;
|
||||
apcm->substream = substream;
|
||||
apcm->interrupt = ct_atc_pcm_interrupt;
|
||||
runtime->private_data = apcm;
|
||||
runtime->private_free = ct_atc_pcm_free_substream;
|
||||
runtime->hw = ct_pcm_capture_hw;
|
||||
runtime->hw.rate_max = atc->rsr * atc->msr;
|
||||
|
||||
err = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
1024, UINT_MAX);
|
||||
if (err < 0) {
|
||||
kfree(apcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
|
||||
if (!apcm->timer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* The ct_atc_pcm object will be freed by runtime->private_free */
|
||||
/* TODO: Notify mixer inactive. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int err;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
err = atc->pcm_capture_prepare(atc, apcm);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
atc->pcm_capture_start(atc, apcm);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
atc->pcm_capture_stop(atc, apcm);
|
||||
break;
|
||||
default:
|
||||
atc->pcm_capture_stop(atc, apcm);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long position;
|
||||
struct ct_atc *atc = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = runtime->private_data;
|
||||
|
||||
/* Read out playback position */
|
||||
position = atc->pcm_capture_position(atc, apcm);
|
||||
position = bytes_to_frames(runtime, position);
|
||||
if (position >= runtime->buffer_size)
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
/* PCM operators for playback */
|
||||
static struct snd_pcm_ops ct_pcm_playback_ops = {
|
||||
.open = ct_pcm_playback_open,
|
||||
.close = ct_pcm_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = ct_pcm_hw_params,
|
||||
.hw_free = ct_pcm_hw_free,
|
||||
.prepare = ct_pcm_playback_prepare,
|
||||
.trigger = ct_pcm_playback_trigger,
|
||||
.pointer = ct_pcm_playback_pointer,
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
/* PCM operators for capture */
|
||||
static struct snd_pcm_ops ct_pcm_capture_ops = {
|
||||
.open = ct_pcm_capture_open,
|
||||
.close = ct_pcm_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = ct_pcm_hw_params,
|
||||
.hw_free = ct_pcm_hw_free,
|
||||
.prepare = ct_pcm_capture_prepare,
|
||||
.trigger = ct_pcm_capture_trigger,
|
||||
.pointer = ct_pcm_capture_pointer,
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
/* Create ALSA pcm device */
|
||||
int ct_alsa_pcm_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
int playback_count, capture_count;
|
||||
|
||||
playback_count = (IEC958 == device) ? 1 : 8;
|
||||
capture_count = (FRONT == device) ? 1 : 0;
|
||||
err = snd_pcm_new(atc->card, "ctxfi", device,
|
||||
playback_count, capture_count, &pcm);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
pcm->private_data = atc;
|
||||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
strlcpy(pcm->name, device_name, sizeof(pcm->name));
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
|
||||
|
||||
if (FRONT == device)
|
||||
snd_pcm_set_ops(pcm,
|
||||
SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||
snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
|
||||
|
||||
return 0;
|
||||
}
|
27
sound/pci/ctxfi/ctpcm.h
Normal file
27
sound/pci/ctxfi/ctpcm.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctpcm.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the pcm device functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTPCM_H
|
||||
#define CTPCM_H
|
||||
|
||||
#include "ctatc.h"
|
||||
|
||||
int ct_alsa_pcm_create(struct ct_atc *atc,
|
||||
enum CTALSADEVS device,
|
||||
const char *device_name);
|
||||
|
||||
#endif /* CTPCM_H */
|
301
sound/pci/ctxfi/ctresource.c
Normal file
301
sound/pci/ctxfi/ctresource.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctresource.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of some generic helper functions.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 15 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define AUDIO_SLOT_BLOCK_NUM 256
|
||||
|
||||
/* Resource allocation based on bit-map management mechanism */
|
||||
static int
|
||||
get_resource(u8 *rscs, unsigned int amount,
|
||||
unsigned int multi, unsigned int *ridx)
|
||||
{
|
||||
int i, j, k, n;
|
||||
|
||||
/* Check whether there are sufficient resources to meet request. */
|
||||
for (i = 0, n = multi; i < amount; i++) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
if (rscs[j] & ((u8)1 << k)) {
|
||||
n = multi;
|
||||
continue;
|
||||
}
|
||||
if (!(--n))
|
||||
break; /* found sufficient contiguous resources */
|
||||
}
|
||||
|
||||
if (i >= amount) {
|
||||
/* Can not find sufficient contiguous resources */
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Mark the contiguous bits in resource bit-map as used */
|
||||
for (n = multi; n > 0; n--) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
rscs[j] |= ((u8)1 << k);
|
||||
i--;
|
||||
}
|
||||
|
||||
*ridx = i + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx)
|
||||
{
|
||||
unsigned int i, j, k, n;
|
||||
|
||||
/* Mark the contiguous bits in resource bit-map as used */
|
||||
for (n = multi, i = idx; n > 0; n--) {
|
||||
j = i / 8;
|
||||
k = i % 8;
|
||||
rscs[j] &= ~((u8)1 << k);
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (n > mgr->avail)
|
||||
return -ENOENT;
|
||||
|
||||
err = get_resource(mgr->rscs, mgr->amount, n, ridx);
|
||||
if (!err)
|
||||
mgr->avail -= n;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
|
||||
{
|
||||
put_resource(mgr->rscs, n, idx);
|
||||
mgr->avail += n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
|
||||
/* SRC channel is at Audio Ring slot 1 every 16 slots. */
|
||||
[SRC] = 0x1,
|
||||
[AMIXER] = 0x4,
|
||||
[SUM] = 0xc,
|
||||
};
|
||||
|
||||
static int rsc_index(const struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int audio_ring_slot(const struct rsc *rsc)
|
||||
{
|
||||
return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
|
||||
}
|
||||
|
||||
static int rsc_next_conj(struct rsc *rsc)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
|
||||
i++;
|
||||
rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
|
||||
return rsc->conj;
|
||||
}
|
||||
|
||||
static int rsc_master(struct rsc *rsc)
|
||||
{
|
||||
return rsc->conj = rsc->idx;
|
||||
}
|
||||
|
||||
static struct rsc_ops rsc_generic_ops = {
|
||||
.index = rsc_index,
|
||||
.output_slot = audio_ring_slot,
|
||||
.master = rsc_master,
|
||||
.next_conj = rsc_next_conj,
|
||||
};
|
||||
|
||||
int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
rsc->idx = idx;
|
||||
rsc->conj = idx;
|
||||
rsc->type = type;
|
||||
rsc->msr = msr;
|
||||
rsc->hw = hw;
|
||||
rsc->ops = &rsc_generic_ops;
|
||||
if (NULL == hw) {
|
||||
rsc->ctrl_blk = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SRC:
|
||||
err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
err = ((struct hw *)hw)->
|
||||
amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
case SUM:
|
||||
case DAIO:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Invalid resource type value %d!\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Failed to get resource control block!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsc_uninit(struct rsc *rsc)
|
||||
{
|
||||
if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
|
||||
switch (rsc->type) {
|
||||
case SRC:
|
||||
((struct hw *)rsc->hw)->
|
||||
src_rsc_put_ctrl_blk(rsc->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
((struct hw *)rsc->hw)->
|
||||
amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
case DAIO:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "ctxfi: "
|
||||
"Invalid resource type value %d!\n", rsc->type);
|
||||
break;
|
||||
}
|
||||
|
||||
rsc->hw = rsc->ctrl_blk = NULL;
|
||||
}
|
||||
|
||||
rsc->idx = rsc->conj = 0;
|
||||
rsc->type = NUM_RSCTYP;
|
||||
rsc->msr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
|
||||
unsigned int amount, void *hw_obj)
|
||||
{
|
||||
int err = 0;
|
||||
struct hw *hw = hw_obj;
|
||||
|
||||
mgr->type = NUM_RSCTYP;
|
||||
|
||||
mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
|
||||
if (NULL == mgr->rscs)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (type) {
|
||||
case SRC:
|
||||
err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk);
|
||||
break;
|
||||
case DAIO:
|
||||
err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Invalid resource type value %d!\n", type);
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR
|
||||
"ctxfi: Failed to get manager control block!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
mgr->type = type;
|
||||
mgr->avail = mgr->amount = amount;
|
||||
mgr->hw = hw;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(mgr->rscs);
|
||||
return err;
|
||||
}
|
||||
|
||||
int rsc_mgr_uninit(struct rsc_mgr *mgr)
|
||||
{
|
||||
if (NULL != mgr->rscs) {
|
||||
kfree(mgr->rscs);
|
||||
mgr->rscs = NULL;
|
||||
}
|
||||
|
||||
if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
|
||||
switch (mgr->type) {
|
||||
case SRC:
|
||||
((struct hw *)mgr->hw)->
|
||||
src_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case SRCIMP:
|
||||
((struct hw *)mgr->hw)->
|
||||
srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case AMIXER:
|
||||
((struct hw *)mgr->hw)->
|
||||
amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case DAIO:
|
||||
((struct hw *)mgr->hw)->
|
||||
daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
|
||||
break;
|
||||
case SUM:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "ctxfi: "
|
||||
"Invalid resource type value %d!\n", mgr->type);
|
||||
break;
|
||||
}
|
||||
|
||||
mgr->hw = mgr->ctrl_blk = NULL;
|
||||
}
|
||||
|
||||
mgr->type = NUM_RSCTYP;
|
||||
mgr->avail = mgr->amount = 0;
|
||||
|
||||
return 0;
|
||||
}
|
72
sound/pci/ctxfi/ctresource.h
Normal file
72
sound/pci/ctxfi/ctresource.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctresource.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of generic hardware resources for
|
||||
* resource management.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTRESOURCE_H
|
||||
#define CTRESOURCE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum RSCTYP {
|
||||
SRC,
|
||||
SRCIMP,
|
||||
AMIXER,
|
||||
SUM,
|
||||
DAIO,
|
||||
NUM_RSCTYP /* This must be the last one and less than 16 */
|
||||
};
|
||||
|
||||
struct rsc_ops;
|
||||
|
||||
struct rsc {
|
||||
u32 idx:12; /* The index of a resource */
|
||||
u32 type:4; /* The type (RSCTYP) of a resource */
|
||||
u32 conj:12; /* Current conjugate index */
|
||||
u32 msr:4; /* The Master Sample Rate a resource working on */
|
||||
void *ctrl_blk; /* Chip specific control info block for a resource */
|
||||
void *hw; /* Chip specific object for hardware access means */
|
||||
struct rsc_ops *ops; /* Generic resource operations */
|
||||
};
|
||||
|
||||
struct rsc_ops {
|
||||
int (*master)(struct rsc *rsc); /* Move to master resource */
|
||||
int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
|
||||
int (*index)(const struct rsc *rsc); /* Return the index of resource */
|
||||
/* Return the output slot number */
|
||||
int (*output_slot)(const struct rsc *rsc);
|
||||
};
|
||||
|
||||
int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
|
||||
int rsc_uninit(struct rsc *rsc);
|
||||
|
||||
struct rsc_mgr {
|
||||
enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
|
||||
unsigned int amount; /* The total amount of a kind of resource */
|
||||
unsigned int avail; /* The amount of currently available resources */
|
||||
unsigned char *rscs; /* The bit-map for resource allocation */
|
||||
void *ctrl_blk; /* Chip specific control info block */
|
||||
void *hw; /* Chip specific object for hardware access */
|
||||
};
|
||||
|
||||
/* Resource management is based on bit-map mechanism */
|
||||
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
|
||||
unsigned int amount, void *hw);
|
||||
int rsc_mgr_uninit(struct rsc_mgr *mgr);
|
||||
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
|
||||
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
|
||||
|
||||
#endif /* CTRESOURCE_H */
|
886
sound/pci/ctxfi/ctsrc.c
Normal file
886
sound/pci/ctxfi/ctsrc.c
Normal file
|
@ -0,0 +1,886 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctsrc.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of the Sample Rate Convertor
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ctsrc.h"
|
||||
#include "cthardware.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define SRC_RESOURCE_NUM 64
|
||||
#define SRCIMP_RESOURCE_NUM 256
|
||||
|
||||
static unsigned int conj_mask;
|
||||
|
||||
static int src_default_config_memrd(struct src *src);
|
||||
static int src_default_config_memwr(struct src *src);
|
||||
static int src_default_config_arcrw(struct src *src);
|
||||
|
||||
static int (*src_default_config[3])(struct src *) = {
|
||||
[MEMRD] = src_default_config_memrd,
|
||||
[MEMWR] = src_default_config_memwr,
|
||||
[ARCRW] = src_default_config_arcrw
|
||||
};
|
||||
|
||||
static int src_set_state(struct src *src, unsigned int state)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_state(src->rsc.ctrl_blk, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_bm(struct src *src, unsigned int bm)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, bm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_sf(struct src *src, unsigned int sf)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, sf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_pm(struct src *src, unsigned int pm)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, pm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_rom(struct src *src, unsigned int rom)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, rom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_vo(struct src *src, unsigned int vo)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, vo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_st(struct src *src, unsigned int st)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_st(src->rsc.ctrl_blk, st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_bp(struct src *src, unsigned int bp)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_bp(src->rsc.ctrl_blk, bp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_cisz(struct src *src, unsigned int cisz)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, cisz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_ca(struct src *src, unsigned int ca)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, ca);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_sa(struct src *src, unsigned int sa)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, sa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_la(struct src *src, unsigned int la)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_la(src->rsc.ctrl_blk, la);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_pitch(struct src *src, unsigned int pitch)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, pitch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_set_clear_zbufs(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_commit_write(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
int i;
|
||||
unsigned int dirty = 0;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
if (src->rsc.msr > 1) {
|
||||
/* Save dirty flags for conjugate resource programming */
|
||||
dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask;
|
||||
}
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
/* Program conjugate parameter mixer resources */
|
||||
if (MEMWR == src->mode)
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < src->rsc.msr; i++) {
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_get_ca(struct src *src)
|
||||
{
|
||||
struct hw *hw;
|
||||
|
||||
hw = src->rsc.hw;
|
||||
return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
|
||||
static int src_init(struct src *src)
|
||||
{
|
||||
src_default_config[src->mode](src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct src *src_next_interleave(struct src *src)
|
||||
{
|
||||
return src->intlv;
|
||||
}
|
||||
|
||||
static int src_default_config_memrd(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
unsigned int rsr, msr;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 1);
|
||||
for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
for (msr = 1; msr < src->rsc.msr; msr++) {
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_default_config_memwr(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 1);
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 1);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_default_config_arcrw(struct src *src)
|
||||
{
|
||||
struct hw *hw = src->rsc.hw;
|
||||
unsigned int rsr, msr;
|
||||
unsigned int dirty;
|
||||
|
||||
hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
|
||||
hw->src_set_bm(src->rsc.ctrl_blk, 0);
|
||||
for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
|
||||
rsr++;
|
||||
|
||||
hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
|
||||
hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32);
|
||||
hw->src_set_wr(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_pm(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_rom(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_vo(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_st(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
|
||||
hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
|
||||
/*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/
|
||||
hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
|
||||
/*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/
|
||||
hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
|
||||
hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
|
||||
hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
|
||||
|
||||
dirty = hw->src_get_dirty(src->rsc.ctrl_blk);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (msr = 0; msr < src->rsc.msr; msr++) {
|
||||
hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
|
||||
hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
|
||||
src->rsc.ctrl_blk);
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct src_rsc_ops src_rsc_ops = {
|
||||
.set_state = src_set_state,
|
||||
.set_bm = src_set_bm,
|
||||
.set_sf = src_set_sf,
|
||||
.set_pm = src_set_pm,
|
||||
.set_rom = src_set_rom,
|
||||
.set_vo = src_set_vo,
|
||||
.set_st = src_set_st,
|
||||
.set_bp = src_set_bp,
|
||||
.set_cisz = src_set_cisz,
|
||||
.set_ca = src_set_ca,
|
||||
.set_sa = src_set_sa,
|
||||
.set_la = src_set_la,
|
||||
.set_pitch = src_set_pitch,
|
||||
.set_clr_zbufs = src_set_clear_zbufs,
|
||||
.commit_write = src_commit_write,
|
||||
.get_ca = src_get_ca,
|
||||
.init = src_init,
|
||||
.next_interleave = src_next_interleave,
|
||||
};
|
||||
|
||||
static int
|
||||
src_rsc_init(struct src *src, u32 idx,
|
||||
const struct src_desc *desc, struct src_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
int i, n;
|
||||
struct src *p;
|
||||
|
||||
n = (MEMRD == desc->mode) ? desc->multi : 1;
|
||||
for (i = 0, p = src; i < n; i++, p++) {
|
||||
err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
/* Initialize src specific rsc operations */
|
||||
p->ops = &src_rsc_ops;
|
||||
p->multi = (0 == i) ? desc->multi : 1;
|
||||
p->mode = desc->mode;
|
||||
src_default_config[desc->mode](p);
|
||||
mgr->src_enable(mgr, p);
|
||||
p->intlv = p + 1;
|
||||
}
|
||||
(--p)->intlv = NULL; /* Set @intlv of the last SRC to NULL */
|
||||
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
for (i--, p--; i >= 0; i--, p--) {
|
||||
mgr->src_disable(mgr, p);
|
||||
rsc_uninit(&p->rsc);
|
||||
}
|
||||
mgr->commit_write(mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int src_rsc_uninit(struct src *src, struct src_mgr *mgr)
|
||||
{
|
||||
int i, n;
|
||||
struct src *p;
|
||||
|
||||
n = (MEMRD == src->mode) ? src->multi : 1;
|
||||
for (i = 0, p = src; i < n; i++, p++) {
|
||||
mgr->src_disable(mgr, p);
|
||||
rsc_uninit(&p->rsc);
|
||||
p->multi = 0;
|
||||
p->ops = NULL;
|
||||
p->mode = NUM_SRCMODES;
|
||||
p->intlv = NULL;
|
||||
}
|
||||
mgr->commit_write(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
|
||||
{
|
||||
unsigned int idx = SRC_RESOURCE_NUM;
|
||||
int err;
|
||||
struct src *src;
|
||||
unsigned long flags;
|
||||
|
||||
*rsrc = NULL;
|
||||
|
||||
/* Check whether there are sufficient src resources to meet request. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
if (MEMRD == desc->mode)
|
||||
err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
|
||||
else
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Allocate mem for master src resource */
|
||||
if (MEMRD == desc->mode)
|
||||
src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
|
||||
else
|
||||
src = kzalloc(sizeof(*src), GFP_KERNEL);
|
||||
|
||||
if (NULL == src) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
err = src_rsc_init(src, idx, desc, mgr);
|
||||
if (err)
|
||||
goto error2;
|
||||
|
||||
*rsrc = src;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
kfree(src);
|
||||
error1:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
if (MEMRD == desc->mode)
|
||||
mgr_put_resource(&mgr->mgr, desc->multi, idx);
|
||||
else
|
||||
mgr_put_resource(&mgr->mgr, 1, idx);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_src_rsc(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
if (MEMRD == src->mode)
|
||||
mgr_put_resource(&mgr->mgr, src->multi,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
else
|
||||
mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
src_rsc_uninit(src, mgr);
|
||||
kfree(src);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_enable_s(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_enable(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_enb_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_disable(struct src_mgr *mgr, struct src *src)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
int i;
|
||||
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
for (i = 0; i < src->rsc.msr; i++) {
|
||||
hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk,
|
||||
src->rsc.ops->index(&src->rsc));
|
||||
src->rsc.ops->next_conj(&src->rsc);
|
||||
}
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_mgr_commit_write(struct src_mgr *mgr)
|
||||
{
|
||||
struct hw *hw = mgr->mgr.hw;
|
||||
|
||||
hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
|
||||
{
|
||||
int err, i;
|
||||
struct src_mgr *src_mgr;
|
||||
|
||||
*rsrc_mgr = NULL;
|
||||
src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
|
||||
if (NULL == src_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&src_mgr->mgr_lock);
|
||||
conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
|
||||
|
||||
src_mgr->get_src = get_src_rsc;
|
||||
src_mgr->put_src = put_src_rsc;
|
||||
src_mgr->src_enable_s = src_enable_s;
|
||||
src_mgr->src_enable = src_enable;
|
||||
src_mgr->src_disable = src_disable;
|
||||
src_mgr->commit_write = src_mgr_commit_write;
|
||||
|
||||
/* Disable all SRC resources. */
|
||||
for (i = 0; i < 256; i++)
|
||||
((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
|
||||
|
||||
((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
|
||||
|
||||
*rsrc_mgr = src_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
kfree(src_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int src_mgr_destroy(struct src_mgr *src_mgr)
|
||||
{
|
||||
rsc_mgr_uninit(&src_mgr->mgr);
|
||||
kfree(src_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SRCIMP resource manager operations */
|
||||
|
||||
static int srcimp_master(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj = 0;
|
||||
return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
|
||||
}
|
||||
|
||||
static int srcimp_next_conj(struct rsc *rsc)
|
||||
{
|
||||
rsc->conj++;
|
||||
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static int srcimp_index(const struct rsc *rsc)
|
||||
{
|
||||
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
|
||||
}
|
||||
|
||||
static struct rsc_ops srcimp_basic_rsc_ops = {
|
||||
.master = srcimp_master,
|
||||
.next_conj = srcimp_next_conj,
|
||||
.index = srcimp_index,
|
||||
.output_slot = NULL,
|
||||
};
|
||||
|
||||
static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input)
|
||||
{
|
||||
struct imapper *entry;
|
||||
int i;
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
src->rsc.ops->master(&src->rsc);
|
||||
input->ops->master(input);
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
for (i = 0; i < srcimp->rsc.msr; i++) {
|
||||
entry = &srcimp->imappers[i];
|
||||
entry->slot = input->ops->output_slot(input);
|
||||
entry->user = src->rsc.ops->index(&src->rsc);
|
||||
entry->addr = srcimp->rsc.ops->index(&srcimp->rsc);
|
||||
srcimp->mgr->imap_add(srcimp->mgr, entry);
|
||||
srcimp->mapped |= (0x1 << i);
|
||||
|
||||
srcimp->rsc.ops->next_conj(&srcimp->rsc);
|
||||
input->ops->next_conj(input);
|
||||
}
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
input->ops->master(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_unmap(struct srcimp *srcimp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Program master and conjugate resources */
|
||||
for (i = 0; i < srcimp->rsc.msr; i++) {
|
||||
if (srcimp->mapped & (0x1 << i)) {
|
||||
srcimp->mgr->imap_delete(srcimp->mgr,
|
||||
&srcimp->imappers[i]);
|
||||
srcimp->mapped &= ~(0x1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct srcimp_rsc_ops srcimp_ops = {
|
||||
.map = srcimp_map,
|
||||
.unmap = srcimp_unmap
|
||||
};
|
||||
|
||||
static int srcimp_rsc_init(struct srcimp *srcimp,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp_mgr *mgr)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rsc_init(&srcimp->rsc, srcimp->idx[0],
|
||||
SRCIMP, desc->msr, mgr->mgr.hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Reserve memory for imapper nodes */
|
||||
srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
|
||||
GFP_KERNEL);
|
||||
if (NULL == srcimp->imappers) {
|
||||
err = -ENOMEM;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
/* Set srcimp specific operations */
|
||||
srcimp->rsc.ops = &srcimp_basic_rsc_ops;
|
||||
srcimp->ops = &srcimp_ops;
|
||||
srcimp->mgr = mgr;
|
||||
|
||||
srcimp->rsc.ops->master(&srcimp->rsc);
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
rsc_uninit(&srcimp->rsc);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int srcimp_rsc_uninit(struct srcimp *srcimp)
|
||||
{
|
||||
if (NULL != srcimp->imappers) {
|
||||
kfree(srcimp->imappers);
|
||||
srcimp->imappers = NULL;
|
||||
}
|
||||
srcimp->ops = NULL;
|
||||
srcimp->mgr = NULL;
|
||||
rsc_uninit(&srcimp->rsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_srcimp_rsc(struct srcimp_mgr *mgr,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp **rsrcimp)
|
||||
{
|
||||
int err, i;
|
||||
unsigned int idx;
|
||||
struct srcimp *srcimp;
|
||||
unsigned long flags;
|
||||
|
||||
*rsrcimp = NULL;
|
||||
|
||||
/* Allocate mem for SRCIMP resource */
|
||||
srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL);
|
||||
if (NULL == srcimp) {
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Check whether there are sufficient SRCIMP resources. */
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < desc->msr; i++) {
|
||||
err = mgr_get_resource(&mgr->mgr, 1, &idx);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
srcimp->idx[i] = idx;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
|
||||
goto error1;
|
||||
}
|
||||
|
||||
err = srcimp_rsc_init(srcimp, desc, mgr);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
*rsrcimp = srcimp;
|
||||
|
||||
return 0;
|
||||
|
||||
error1:
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i--; i >= 0; i--)
|
||||
mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
kfree(srcimp);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&mgr->mgr_lock, flags);
|
||||
for (i = 0; i < srcimp->rsc.msr; i++)
|
||||
mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
|
||||
|
||||
spin_unlock_irqrestore(&mgr->mgr_lock, flags);
|
||||
srcimp_rsc_uninit(srcimp);
|
||||
kfree(srcimp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_map_op(void *data, struct imapper *entry)
|
||||
{
|
||||
struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr;
|
||||
struct hw *hw = mgr->hw;
|
||||
|
||||
hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
|
||||
hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user);
|
||||
hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
|
||||
hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
|
||||
hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
if ((0 == entry->addr) && (mgr->init_imap_added)) {
|
||||
input_mapper_delete(&mgr->imappers,
|
||||
mgr->init_imap, srcimp_map_op, mgr);
|
||||
mgr->init_imap_added = 0;
|
||||
}
|
||||
err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&mgr->imap_lock, flags);
|
||||
err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
|
||||
if (list_empty(&mgr->imappers)) {
|
||||
input_mapper_add(&mgr->imappers, mgr->init_imap,
|
||||
srcimp_map_op, mgr);
|
||||
mgr->init_imap_added = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&mgr->imap_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
|
||||
{
|
||||
int err;
|
||||
struct srcimp_mgr *srcimp_mgr;
|
||||
struct imapper *entry;
|
||||
|
||||
*rsrcimp_mgr = NULL;
|
||||
srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
|
||||
if (NULL == srcimp_mgr)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
|
||||
if (err)
|
||||
goto error1;
|
||||
|
||||
spin_lock_init(&srcimp_mgr->mgr_lock);
|
||||
spin_lock_init(&srcimp_mgr->imap_lock);
|
||||
INIT_LIST_HEAD(&srcimp_mgr->imappers);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (NULL == entry) {
|
||||
err = -ENOMEM;
|
||||
goto error2;
|
||||
}
|
||||
entry->slot = entry->addr = entry->next = entry->user = 0;
|
||||
list_add(&entry->list, &srcimp_mgr->imappers);
|
||||
srcimp_mgr->init_imap = entry;
|
||||
srcimp_mgr->init_imap_added = 1;
|
||||
|
||||
srcimp_mgr->get_srcimp = get_srcimp_rsc;
|
||||
srcimp_mgr->put_srcimp = put_srcimp_rsc;
|
||||
srcimp_mgr->imap_add = srcimp_imap_add;
|
||||
srcimp_mgr->imap_delete = srcimp_imap_delete;
|
||||
|
||||
*rsrcimp_mgr = srcimp_mgr;
|
||||
|
||||
return 0;
|
||||
|
||||
error2:
|
||||
rsc_mgr_uninit(&srcimp_mgr->mgr);
|
||||
error1:
|
||||
kfree(srcimp_mgr);
|
||||
return err;
|
||||
}
|
||||
|
||||
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* free src input mapper list */
|
||||
spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
|
||||
free_input_mapper_list(&srcimp_mgr->imappers);
|
||||
spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
|
||||
|
||||
rsc_mgr_uninit(&srcimp_mgr->mgr);
|
||||
kfree(srcimp_mgr);
|
||||
|
||||
return 0;
|
||||
}
|
149
sound/pci/ctxfi/ctsrc.h
Normal file
149
sound/pci/ctxfi/ctsrc.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctsrc.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of the Sample Rate Convertor
|
||||
* resource management object.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date May 13 2008
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CTSRC_H
|
||||
#define CTSRC_H
|
||||
|
||||
#include "ctresource.h"
|
||||
#include "ctimap.h"
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define SRC_STATE_OFF 0x0
|
||||
#define SRC_STATE_INIT 0x4
|
||||
#define SRC_STATE_RUN 0x5
|
||||
|
||||
#define SRC_SF_U8 0x0
|
||||
#define SRC_SF_S16 0x1
|
||||
#define SRC_SF_S24 0x2
|
||||
#define SRC_SF_S32 0x3
|
||||
#define SRC_SF_F32 0x4
|
||||
|
||||
/* Define the descriptor of a src resource */
|
||||
enum SRCMODE {
|
||||
MEMRD, /* Read data from host memory */
|
||||
MEMWR, /* Write data to host memory */
|
||||
ARCRW, /* Read from and write to audio ring channel */
|
||||
NUM_SRCMODES
|
||||
};
|
||||
|
||||
struct src_rsc_ops;
|
||||
|
||||
struct src {
|
||||
struct rsc rsc; /* Basic resource info */
|
||||
struct src *intlv; /* Pointer to next interleaved SRC in a series */
|
||||
struct src_rsc_ops *ops; /* SRC specific operations */
|
||||
/* Number of contiguous srcs for interleaved usage */
|
||||
unsigned char multi;
|
||||
unsigned char mode; /* Working mode of this SRC resource */
|
||||
};
|
||||
|
||||
struct src_rsc_ops {
|
||||
int (*set_state)(struct src *src, unsigned int state);
|
||||
int (*set_bm)(struct src *src, unsigned int bm);
|
||||
int (*set_sf)(struct src *src, unsigned int sf);
|
||||
int (*set_pm)(struct src *src, unsigned int pm);
|
||||
int (*set_rom)(struct src *src, unsigned int rom);
|
||||
int (*set_vo)(struct src *src, unsigned int vo);
|
||||
int (*set_st)(struct src *src, unsigned int st);
|
||||
int (*set_bp)(struct src *src, unsigned int bp);
|
||||
int (*set_cisz)(struct src *src, unsigned int cisz);
|
||||
int (*set_ca)(struct src *src, unsigned int ca);
|
||||
int (*set_sa)(struct src *src, unsigned int sa);
|
||||
int (*set_la)(struct src *src, unsigned int la);
|
||||
int (*set_pitch)(struct src *src, unsigned int pitch);
|
||||
int (*set_clr_zbufs)(struct src *src);
|
||||
int (*commit_write)(struct src *src);
|
||||
int (*get_ca)(struct src *src);
|
||||
int (*init)(struct src *src);
|
||||
struct src* (*next_interleave)(struct src *src);
|
||||
};
|
||||
|
||||
/* Define src resource request description info */
|
||||
struct src_desc {
|
||||
/* Number of contiguous master srcs for interleaved usage */
|
||||
unsigned char multi;
|
||||
unsigned char msr;
|
||||
unsigned char mode; /* Working mode of the requested srcs */
|
||||
};
|
||||
|
||||
/* Define src manager object */
|
||||
struct src_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
|
||||
/* request src resource */
|
||||
int (*get_src)(struct src_mgr *mgr,
|
||||
const struct src_desc *desc, struct src **rsrc);
|
||||
/* return src resource */
|
||||
int (*put_src)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_enable)(struct src_mgr *mgr, struct src *src);
|
||||
int (*src_disable)(struct src_mgr *mgr, struct src *src);
|
||||
int (*commit_write)(struct src_mgr *mgr);
|
||||
};
|
||||
|
||||
/* Define the descriptor of a SRC Input Mapper resource */
|
||||
struct srcimp_mgr;
|
||||
struct srcimp_rsc_ops;
|
||||
|
||||
struct srcimp {
|
||||
struct rsc rsc;
|
||||
unsigned char idx[8];
|
||||
struct imapper *imappers;
|
||||
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
|
||||
struct srcimp_mgr *mgr;
|
||||
struct srcimp_rsc_ops *ops;
|
||||
};
|
||||
|
||||
struct srcimp_rsc_ops {
|
||||
int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
|
||||
int (*unmap)(struct srcimp *srcimp);
|
||||
};
|
||||
|
||||
/* Define SRCIMP resource request description info */
|
||||
struct srcimp_desc {
|
||||
unsigned int msr;
|
||||
};
|
||||
|
||||
struct srcimp_mgr {
|
||||
struct rsc_mgr mgr; /* Basic resource manager info */
|
||||
spinlock_t mgr_lock;
|
||||
spinlock_t imap_lock;
|
||||
struct list_head imappers;
|
||||
struct imapper *init_imap;
|
||||
unsigned int init_imap_added;
|
||||
|
||||
/* request srcimp resource */
|
||||
int (*get_srcimp)(struct srcimp_mgr *mgr,
|
||||
const struct srcimp_desc *desc,
|
||||
struct srcimp **rsrcimp);
|
||||
/* return srcimp resource */
|
||||
int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
|
||||
int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
|
||||
int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
|
||||
};
|
||||
|
||||
/* Constructor and destructor of SRC resource manager */
|
||||
int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
|
||||
int src_mgr_destroy(struct src_mgr *src_mgr);
|
||||
/* Constructor and destructor of SRCIMP resource manager */
|
||||
int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
|
||||
int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
|
||||
|
||||
#endif /* CTSRC_H */
|
441
sound/pci/ctxfi/cttimer.c
Normal file
441
sound/pci/ctxfi/cttimer.c
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* PCM timer handling on ctxfi
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "ctatc.h"
|
||||
#include "cthardware.h"
|
||||
#include "cttimer.h"
|
||||
|
||||
static int use_system_timer;
|
||||
MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
|
||||
module_param(use_system_timer, bool, S_IRUGO);
|
||||
|
||||
struct ct_timer_ops {
|
||||
void (*init)(struct ct_timer_instance *);
|
||||
void (*prepare)(struct ct_timer_instance *);
|
||||
void (*start)(struct ct_timer_instance *);
|
||||
void (*stop)(struct ct_timer_instance *);
|
||||
void (*free_instance)(struct ct_timer_instance *);
|
||||
void (*interrupt)(struct ct_timer *);
|
||||
void (*free_global)(struct ct_timer *);
|
||||
};
|
||||
|
||||
/* timer instance -- assigned to each PCM stream */
|
||||
struct ct_timer_instance {
|
||||
spinlock_t lock;
|
||||
struct ct_timer *timer_base;
|
||||
struct ct_atc_pcm *apcm;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct timer_list timer;
|
||||
struct list_head instance_list;
|
||||
struct list_head running_list;
|
||||
unsigned int position;
|
||||
unsigned int frag_count;
|
||||
unsigned int running:1;
|
||||
unsigned int need_update:1;
|
||||
};
|
||||
|
||||
/* timer instance manager */
|
||||
struct ct_timer {
|
||||
spinlock_t lock; /* global timer lock (for xfitimer) */
|
||||
spinlock_t list_lock; /* lock for instance list */
|
||||
struct ct_atc *atc;
|
||||
struct ct_timer_ops *ops;
|
||||
struct list_head instance_head;
|
||||
struct list_head running_head;
|
||||
unsigned int wc; /* current wallclock */
|
||||
unsigned int irq_handling:1; /* in IRQ handling */
|
||||
unsigned int reprogram:1; /* need to reprogram the internval */
|
||||
unsigned int running:1; /* global timer running */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* system-timer-based updates
|
||||
*/
|
||||
|
||||
static void ct_systimer_callback(unsigned long data)
|
||||
{
|
||||
struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
|
||||
struct snd_pcm_substream *substream = ti->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ct_atc_pcm *apcm = ti->apcm;
|
||||
unsigned int period_size = runtime->period_size;
|
||||
unsigned int buffer_size = runtime->buffer_size;
|
||||
unsigned long flags;
|
||||
unsigned int position, dist, interval;
|
||||
|
||||
position = substream->ops->pointer(substream);
|
||||
dist = (position + buffer_size - ti->position) % buffer_size;
|
||||
if (dist >= period_size ||
|
||||
position / period_size != ti->position / period_size) {
|
||||
apcm->interrupt(apcm);
|
||||
ti->position = position;
|
||||
}
|
||||
/* Add extra HZ*5/1000 to avoid overrun issue when recording
|
||||
* at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
|
||||
interval = ((period_size - (position % period_size))
|
||||
* HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
|
||||
spin_lock_irqsave(&ti->lock, flags);
|
||||
if (ti->running)
|
||||
mod_timer(&ti->timer, jiffies + interval);
|
||||
spin_unlock_irqrestore(&ti->lock, flags);
|
||||
}
|
||||
|
||||
static void ct_systimer_init(struct ct_timer_instance *ti)
|
||||
{
|
||||
setup_timer(&ti->timer, ct_systimer_callback,
|
||||
(unsigned long)ti);
|
||||
}
|
||||
|
||||
static void ct_systimer_start(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = ti->substream->runtime;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ti->lock, flags);
|
||||
ti->running = 1;
|
||||
mod_timer(&ti->timer,
|
||||
jiffies + (runtime->period_size * HZ +
|
||||
(runtime->rate - 1)) / runtime->rate);
|
||||
spin_unlock_irqrestore(&ti->lock, flags);
|
||||
}
|
||||
|
||||
static void ct_systimer_stop(struct ct_timer_instance *ti)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ti->lock, flags);
|
||||
ti->running = 0;
|
||||
del_timer(&ti->timer);
|
||||
spin_unlock_irqrestore(&ti->lock, flags);
|
||||
}
|
||||
|
||||
static void ct_systimer_prepare(struct ct_timer_instance *ti)
|
||||
{
|
||||
ct_systimer_stop(ti);
|
||||
try_to_del_timer_sync(&ti->timer);
|
||||
}
|
||||
|
||||
#define ct_systimer_free ct_systimer_prepare
|
||||
|
||||
static struct ct_timer_ops ct_systimer_ops = {
|
||||
.init = ct_systimer_init,
|
||||
.free_instance = ct_systimer_free,
|
||||
.prepare = ct_systimer_prepare,
|
||||
.start = ct_systimer_start,
|
||||
.stop = ct_systimer_stop,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Handling multiple streams using a global emu20k1 timer irq
|
||||
*/
|
||||
|
||||
#define CT_TIMER_FREQ 48000
|
||||
#define MIN_TICKS 1
|
||||
#define MAX_TICKS ((1 << 13) - 1)
|
||||
|
||||
static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
|
||||
{
|
||||
struct hw *hw = atimer->atc->hw;
|
||||
if (ticks > MAX_TICKS)
|
||||
ticks = MAX_TICKS;
|
||||
hw->set_timer_tick(hw, ticks);
|
||||
if (!atimer->running)
|
||||
hw->set_timer_irq(hw, 1);
|
||||
atimer->running = 1;
|
||||
}
|
||||
|
||||
static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
|
||||
{
|
||||
if (atimer->running) {
|
||||
struct hw *hw = atimer->atc->hw;
|
||||
hw->set_timer_irq(hw, 0);
|
||||
hw->set_timer_tick(hw, 0);
|
||||
atimer->running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
|
||||
{
|
||||
struct hw *hw = atimer->atc->hw;
|
||||
return hw->get_wc(hw);
|
||||
}
|
||||
|
||||
/*
|
||||
* reprogram the timer interval;
|
||||
* checks the running instance list and determines the next timer interval.
|
||||
* also updates the each stream position, returns the number of streams
|
||||
* to call snd_pcm_period_elapsed() appropriately
|
||||
*
|
||||
* call this inside the lock and irq disabled
|
||||
*/
|
||||
static int ct_xfitimer_reprogram(struct ct_timer *atimer)
|
||||
{
|
||||
struct ct_timer_instance *ti;
|
||||
unsigned int min_intr = (unsigned int)-1;
|
||||
int updates = 0;
|
||||
unsigned int wc, diff;
|
||||
|
||||
if (list_empty(&atimer->running_head)) {
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
atimer->reprogram = 0; /* clear flag */
|
||||
return 0;
|
||||
}
|
||||
|
||||
wc = ct_xfitimer_get_wc(atimer);
|
||||
diff = wc - atimer->wc;
|
||||
atimer->wc = wc;
|
||||
list_for_each_entry(ti, &atimer->running_head, running_list) {
|
||||
if (ti->frag_count > diff)
|
||||
ti->frag_count -= diff;
|
||||
else {
|
||||
unsigned int pos;
|
||||
unsigned int period_size, rate;
|
||||
|
||||
period_size = ti->substream->runtime->period_size;
|
||||
rate = ti->substream->runtime->rate;
|
||||
pos = ti->substream->ops->pointer(ti->substream);
|
||||
if (pos / period_size != ti->position / period_size) {
|
||||
ti->need_update = 1;
|
||||
ti->position = pos;
|
||||
updates++;
|
||||
}
|
||||
pos %= period_size;
|
||||
pos = period_size - pos;
|
||||
ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
|
||||
rate - 1, rate);
|
||||
}
|
||||
if (ti->frag_count < min_intr)
|
||||
min_intr = ti->frag_count;
|
||||
}
|
||||
|
||||
if (min_intr < MIN_TICKS)
|
||||
min_intr = MIN_TICKS;
|
||||
ct_xfitimer_irq_rearm(atimer, min_intr);
|
||||
atimer->reprogram = 0; /* clear flag */
|
||||
return updates;
|
||||
}
|
||||
|
||||
/* look through the instance list and call period_elapsed if needed */
|
||||
static void ct_xfitimer_check_period(struct ct_timer *atimer)
|
||||
{
|
||||
struct ct_timer_instance *ti;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atimer->list_lock, flags);
|
||||
list_for_each_entry(ti, &atimer->instance_head, instance_list) {
|
||||
if (ti->need_update) {
|
||||
ti->need_update = 0;
|
||||
ti->apcm->interrupt(ti->apcm);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&atimer->list_lock, flags);
|
||||
}
|
||||
|
||||
/* Handle timer-interrupt */
|
||||
static void ct_xfitimer_callback(struct ct_timer *atimer)
|
||||
{
|
||||
int update;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
atimer->irq_handling = 1;
|
||||
do {
|
||||
update = ct_xfitimer_reprogram(atimer);
|
||||
spin_unlock(&atimer->lock);
|
||||
if (update)
|
||||
ct_xfitimer_check_period(atimer);
|
||||
spin_lock(&atimer->lock);
|
||||
} while (atimer->reprogram);
|
||||
atimer->irq_handling = 0;
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
}
|
||||
|
||||
static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
|
||||
{
|
||||
ti->frag_count = ti->substream->runtime->period_size;
|
||||
ti->need_update = 0;
|
||||
}
|
||||
|
||||
|
||||
/* start/stop the timer */
|
||||
static void ct_xfitimer_update(struct ct_timer *atimer)
|
||||
{
|
||||
unsigned long flags;
|
||||
int update;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
if (atimer->irq_handling) {
|
||||
/* reached from IRQ handler; let it handle later */
|
||||
atimer->reprogram = 1;
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
update = ct_xfitimer_reprogram(atimer);
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
if (update)
|
||||
ct_xfitimer_check_period(atimer);
|
||||
}
|
||||
|
||||
static void ct_xfitimer_start(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct ct_timer *atimer = ti->timer_base;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
if (list_empty(&ti->running_list))
|
||||
atimer->wc = ct_xfitimer_get_wc(atimer);
|
||||
list_add(&ti->running_list, &atimer->running_head);
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
ct_xfitimer_update(atimer);
|
||||
}
|
||||
|
||||
static void ct_xfitimer_stop(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct ct_timer *atimer = ti->timer_base;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
list_del_init(&ti->running_list);
|
||||
ti->need_update = 0;
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
ct_xfitimer_update(atimer);
|
||||
}
|
||||
|
||||
static void ct_xfitimer_free_global(struct ct_timer *atimer)
|
||||
{
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
}
|
||||
|
||||
static struct ct_timer_ops ct_xfitimer_ops = {
|
||||
.prepare = ct_xfitimer_prepare,
|
||||
.start = ct_xfitimer_start,
|
||||
.stop = ct_xfitimer_stop,
|
||||
.interrupt = ct_xfitimer_callback,
|
||||
.free_global = ct_xfitimer_free_global,
|
||||
};
|
||||
|
||||
/*
|
||||
* timer instance
|
||||
*/
|
||||
|
||||
struct ct_timer_instance *
|
||||
ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
|
||||
{
|
||||
struct ct_timer_instance *ti;
|
||||
|
||||
ti = kzalloc(sizeof(*ti), GFP_KERNEL);
|
||||
if (!ti)
|
||||
return NULL;
|
||||
spin_lock_init(&ti->lock);
|
||||
INIT_LIST_HEAD(&ti->instance_list);
|
||||
INIT_LIST_HEAD(&ti->running_list);
|
||||
ti->timer_base = atimer;
|
||||
ti->apcm = apcm;
|
||||
ti->substream = apcm->substream;
|
||||
if (atimer->ops->init)
|
||||
atimer->ops->init(ti);
|
||||
|
||||
spin_lock_irq(&atimer->list_lock);
|
||||
list_add(&ti->instance_list, &atimer->instance_head);
|
||||
spin_unlock_irq(&atimer->list_lock);
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
void ct_timer_prepare(struct ct_timer_instance *ti)
|
||||
{
|
||||
if (ti->timer_base->ops->prepare)
|
||||
ti->timer_base->ops->prepare(ti);
|
||||
ti->position = 0;
|
||||
ti->running = 0;
|
||||
}
|
||||
|
||||
void ct_timer_start(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct ct_timer *atimer = ti->timer_base;
|
||||
atimer->ops->start(ti);
|
||||
}
|
||||
|
||||
void ct_timer_stop(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct ct_timer *atimer = ti->timer_base;
|
||||
atimer->ops->stop(ti);
|
||||
}
|
||||
|
||||
void ct_timer_instance_free(struct ct_timer_instance *ti)
|
||||
{
|
||||
struct ct_timer *atimer = ti->timer_base;
|
||||
|
||||
atimer->ops->stop(ti); /* to be sure */
|
||||
if (atimer->ops->free_instance)
|
||||
atimer->ops->free_instance(ti);
|
||||
|
||||
spin_lock_irq(&atimer->list_lock);
|
||||
list_del(&ti->instance_list);
|
||||
spin_unlock_irq(&atimer->list_lock);
|
||||
|
||||
kfree(ti);
|
||||
}
|
||||
|
||||
/*
|
||||
* timer manager
|
||||
*/
|
||||
|
||||
static void ct_timer_interrupt(void *data, unsigned int status)
|
||||
{
|
||||
struct ct_timer *timer = data;
|
||||
|
||||
/* Interval timer interrupt */
|
||||
if ((status & IT_INT) && timer->ops->interrupt)
|
||||
timer->ops->interrupt(timer);
|
||||
}
|
||||
|
||||
struct ct_timer *ct_timer_new(struct ct_atc *atc)
|
||||
{
|
||||
struct ct_timer *atimer;
|
||||
struct hw *hw;
|
||||
|
||||
atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
|
||||
if (!atimer)
|
||||
return NULL;
|
||||
spin_lock_init(&atimer->lock);
|
||||
spin_lock_init(&atimer->list_lock);
|
||||
INIT_LIST_HEAD(&atimer->instance_head);
|
||||
INIT_LIST_HEAD(&atimer->running_head);
|
||||
atimer->atc = atc;
|
||||
hw = atc->hw;
|
||||
if (!use_system_timer && hw->set_timer_irq) {
|
||||
snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
|
||||
atimer->ops = &ct_xfitimer_ops;
|
||||
hw->irq_callback_data = atimer;
|
||||
hw->irq_callback = ct_timer_interrupt;
|
||||
} else {
|
||||
snd_printd(KERN_INFO "ctxfi: Use system timer\n");
|
||||
atimer->ops = &ct_systimer_ops;
|
||||
}
|
||||
return atimer;
|
||||
}
|
||||
|
||||
void ct_timer_free(struct ct_timer *atimer)
|
||||
{
|
||||
struct hw *hw = atimer->atc->hw;
|
||||
hw->irq_callback = NULL;
|
||||
if (atimer->ops->free_global)
|
||||
atimer->ops->free_global(atimer);
|
||||
kfree(atimer);
|
||||
}
|
||||
|
29
sound/pci/ctxfi/cttimer.h
Normal file
29
sound/pci/ctxfi/cttimer.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Timer handling
|
||||
*/
|
||||
|
||||
#ifndef __CTTIMER_H
|
||||
#define __CTTIMER_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct snd_pcm_substream;
|
||||
struct ct_atc;
|
||||
struct ct_atc_pcm;
|
||||
|
||||
struct ct_timer;
|
||||
struct ct_timer_instance;
|
||||
|
||||
struct ct_timer *ct_timer_new(struct ct_atc *atc);
|
||||
void ct_timer_free(struct ct_timer *atimer);
|
||||
|
||||
struct ct_timer_instance *
|
||||
ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm);
|
||||
void ct_timer_instance_free(struct ct_timer_instance *ti);
|
||||
void ct_timer_start(struct ct_timer_instance *ti);
|
||||
void ct_timer_stop(struct ct_timer_instance *ti);
|
||||
void ct_timer_prepare(struct ct_timer_instance *ti);
|
||||
|
||||
#endif /* __CTTIMER_H */
|
250
sound/pci/ctxfi/ctvmem.c
Normal file
250
sound/pci/ctxfi/ctvmem.c
Normal file
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctvmem.c
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the implementation of virtual memory management object
|
||||
* for card device.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Apr 1 2008
|
||||
*/
|
||||
|
||||
#include "ctvmem.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/io.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *))
|
||||
#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE)
|
||||
|
||||
/* *
|
||||
* Find or create vm block based on requested @size.
|
||||
* @size must be page aligned.
|
||||
* */
|
||||
static struct ct_vm_block *
|
||||
get_vm_block(struct ct_vm *vm, unsigned int size)
|
||||
{
|
||||
struct ct_vm_block *block = NULL, *entry;
|
||||
struct list_head *pos;
|
||||
|
||||
size = CT_PAGE_ALIGN(size);
|
||||
if (size > vm->size) {
|
||||
printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
|
||||
"memory space available!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
list_for_each(pos, &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
if (entry->size >= size)
|
||||
break; /* found a block that is big enough */
|
||||
}
|
||||
if (pos == &vm->unused)
|
||||
goto out;
|
||||
|
||||
if (entry->size == size) {
|
||||
/* Move the vm node from unused list to used list directly */
|
||||
list_del(&entry->list);
|
||||
list_add(&entry->list, &vm->used);
|
||||
vm->size -= size;
|
||||
block = entry;
|
||||
goto out;
|
||||
}
|
||||
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (NULL == block)
|
||||
goto out;
|
||||
|
||||
block->addr = entry->addr;
|
||||
block->size = size;
|
||||
list_add(&block->list, &vm->used);
|
||||
entry->addr += size;
|
||||
entry->size -= size;
|
||||
vm->size -= size;
|
||||
|
||||
out:
|
||||
mutex_unlock(&vm->lock);
|
||||
return block;
|
||||
}
|
||||
|
||||
static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
|
||||
{
|
||||
struct ct_vm_block *entry, *pre_ent;
|
||||
struct list_head *pos, *pre;
|
||||
|
||||
block->size = CT_PAGE_ALIGN(block->size);
|
||||
|
||||
mutex_lock(&vm->lock);
|
||||
list_del(&block->list);
|
||||
vm->size += block->size;
|
||||
|
||||
list_for_each(pos, &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
if (entry->addr >= (block->addr + block->size))
|
||||
break; /* found a position */
|
||||
}
|
||||
if (pos == &vm->unused) {
|
||||
list_add_tail(&block->list, &vm->unused);
|
||||
entry = block;
|
||||
} else {
|
||||
if ((block->addr + block->size) == entry->addr) {
|
||||
entry->addr = block->addr;
|
||||
entry->size += block->size;
|
||||
kfree(block);
|
||||
} else {
|
||||
__list_add(&block->list, pos->prev, pos);
|
||||
entry = block;
|
||||
}
|
||||
}
|
||||
|
||||
pos = &entry->list;
|
||||
pre = pos->prev;
|
||||
while (pre != &vm->unused) {
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
pre_ent = list_entry(pre, struct ct_vm_block, list);
|
||||
if ((pre_ent->addr + pre_ent->size) > entry->addr)
|
||||
break;
|
||||
|
||||
pre_ent->size += entry->size;
|
||||
list_del(pos);
|
||||
kfree(entry);
|
||||
pos = pre;
|
||||
pre = pos->prev;
|
||||
}
|
||||
mutex_unlock(&vm->lock);
|
||||
}
|
||||
|
||||
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
|
||||
static struct ct_vm_block *
|
||||
ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
|
||||
{
|
||||
struct ct_vm_block *block;
|
||||
unsigned int pte_start;
|
||||
unsigned i, pages;
|
||||
unsigned long *ptp;
|
||||
|
||||
block = get_vm_block(vm, size);
|
||||
if (block == NULL) {
|
||||
printk(KERN_ERR "ctxfi: No virtual memory block that is big "
|
||||
"enough to allocate!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptp = vm->ptp[0];
|
||||
pte_start = (block->addr >> CT_PAGE_SHIFT);
|
||||
pages = block->size >> CT_PAGE_SHIFT;
|
||||
for (i = 0; i < pages; i++) {
|
||||
unsigned long addr;
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT);
|
||||
ptp[pte_start + i] = addr;
|
||||
}
|
||||
|
||||
block->size = size;
|
||||
return block;
|
||||
}
|
||||
|
||||
static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
|
||||
{
|
||||
/* do unmapping */
|
||||
put_vm_block(vm, block);
|
||||
}
|
||||
|
||||
/* *
|
||||
* return the host (kmalloced) addr of the @index-th device
|
||||
* page talbe page on success, or NULL on failure.
|
||||
* The first returned NULL indicates the termination.
|
||||
* */
|
||||
static void *
|
||||
ct_get_ptp_virt(struct ct_vm *vm, int index)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index];
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
int ct_vm_create(struct ct_vm **rvm)
|
||||
{
|
||||
struct ct_vm *vm;
|
||||
struct ct_vm_block *block;
|
||||
int i;
|
||||
|
||||
*rvm = NULL;
|
||||
|
||||
vm = kzalloc(sizeof(*vm), GFP_KERNEL);
|
||||
if (NULL == vm)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&vm->lock);
|
||||
|
||||
/* Allocate page table pages */
|
||||
for (i = 0; i < CT_PTP_NUM; i++) {
|
||||
vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (NULL == vm->ptp[i])
|
||||
break;
|
||||
}
|
||||
if (!i) {
|
||||
/* no page table pages are allocated */
|
||||
kfree(vm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
vm->size = CT_ADDRS_PER_PAGE * i;
|
||||
/* Initialise remaining ptps */
|
||||
for (; i < CT_PTP_NUM; i++)
|
||||
vm->ptp[i] = NULL;
|
||||
|
||||
vm->map = ct_vm_map;
|
||||
vm->unmap = ct_vm_unmap;
|
||||
vm->get_ptp_virt = ct_get_ptp_virt;
|
||||
INIT_LIST_HEAD(&vm->unused);
|
||||
INIT_LIST_HEAD(&vm->used);
|
||||
block = kzalloc(sizeof(*block), GFP_KERNEL);
|
||||
if (NULL != block) {
|
||||
block->addr = 0;
|
||||
block->size = vm->size;
|
||||
list_add(&block->list, &vm->unused);
|
||||
}
|
||||
|
||||
*rvm = vm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The caller must ensure no mapping pages are being used
|
||||
* by hardware before calling this function */
|
||||
void ct_vm_destroy(struct ct_vm *vm)
|
||||
{
|
||||
int i;
|
||||
struct list_head *pos;
|
||||
struct ct_vm_block *entry;
|
||||
|
||||
/* free used and unused list nodes */
|
||||
while (!list_empty(&vm->used)) {
|
||||
pos = vm->used.next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
kfree(entry);
|
||||
}
|
||||
while (!list_empty(&vm->unused)) {
|
||||
pos = vm->unused.next;
|
||||
list_del(pos);
|
||||
entry = list_entry(pos, struct ct_vm_block, list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
/* free allocated page table pages */
|
||||
for (i = 0; i < CT_PTP_NUM; i++)
|
||||
kfree(vm->ptp[i]);
|
||||
|
||||
vm->size = 0;
|
||||
|
||||
kfree(vm);
|
||||
}
|
61
sound/pci/ctxfi/ctvmem.h
Normal file
61
sound/pci/ctxfi/ctvmem.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*
|
||||
* @File ctvmem.h
|
||||
*
|
||||
* @Brief
|
||||
* This file contains the definition of virtual memory management object
|
||||
* for card device.
|
||||
*
|
||||
* @Author Liu Chun
|
||||
* @Date Mar 28 2008
|
||||
*/
|
||||
|
||||
#ifndef CTVMEM_H
|
||||
#define CTVMEM_H
|
||||
|
||||
#define CT_PTP_NUM 1 /* num of device page table pages */
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* The chip can handle the page table of 4k pages
|
||||
* (emu20k1 can handle even 8k pages, but we don't use it right now)
|
||||
*/
|
||||
#define CT_PAGE_SIZE 4096
|
||||
#define CT_PAGE_SHIFT 12
|
||||
#define CT_PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
#define CT_PAGE_ALIGN(addr) ALIGN(addr, CT_PAGE_SIZE)
|
||||
|
||||
struct ct_vm_block {
|
||||
unsigned int addr; /* starting logical addr of this block */
|
||||
unsigned int size; /* size of this device virtual mem block */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct snd_pcm_substream;
|
||||
|
||||
/* Virtual memory management object for card device */
|
||||
struct ct_vm {
|
||||
void *ptp[CT_PTP_NUM]; /* Device page table pages */
|
||||
unsigned int size; /* Available addr space in bytes */
|
||||
struct list_head unused; /* List of unused blocks */
|
||||
struct list_head used; /* List of used blocks */
|
||||
struct mutex lock;
|
||||
|
||||
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
|
||||
struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *,
|
||||
int size);
|
||||
/* Unmap device logical addr area. */
|
||||
void (*unmap)(struct ct_vm *, struct ct_vm_block *block);
|
||||
void *(*get_ptp_virt)(struct ct_vm *vm, int index);
|
||||
};
|
||||
|
||||
int ct_vm_create(struct ct_vm **rvm);
|
||||
void ct_vm_destroy(struct ct_vm *vm);
|
||||
|
||||
#endif /* CTVMEM_H */
|
142
sound/pci/ctxfi/xfi.c
Normal file
142
sound/pci/ctxfi/xfi.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* xfi linux driver.
|
||||
*
|
||||
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
|
||||
*
|
||||
* This source file is released under GPL v2 license (no other versions).
|
||||
* See the COPYING file included in the main directory of this source
|
||||
* distribution for the license terms and conditions.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include "ctatc.h"
|
||||
#include "cthardware.h"
|
||||
|
||||
MODULE_AUTHOR("Creative Technology Ltd");
|
||||
MODULE_DESCRIPTION("X-Fi driver version 1.03");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
|
||||
|
||||
static unsigned int reference_rate = 48000;
|
||||
static unsigned int multiple = 2;
|
||||
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
|
||||
module_param(reference_rate, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
|
||||
module_param(multiple, uint, S_IRUGO);
|
||||
|
||||
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;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
|
||||
|
||||
static struct pci_device_id ct_pci_dev_ids[] = {
|
||||
/* only X-Fi is supported, so... */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
|
||||
.driver_data = ATC20K1,
|
||||
},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2),
|
||||
.driver_data = ATC20K2,
|
||||
},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
|
||||
|
||||
static int __devinit
|
||||
ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
struct snd_card *card;
|
||||
struct ct_atc *atc;
|
||||
int err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
|
||||
if (!enable[dev]) {
|
||||
dev++;
|
||||
return -ENOENT;
|
||||
}
|
||||
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
|
||||
if (err)
|
||||
return err;
|
||||
if ((reference_rate != 48000) && (reference_rate != 44100)) {
|
||||
printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
|
||||
reference_rate);
|
||||
printk(KERN_ERR "ctxfi: The valid values for reference_rate "
|
||||
"are 48000 and 44100, Value 48000 is assumed.\n");
|
||||
reference_rate = 48000;
|
||||
}
|
||||
if ((multiple != 1) && (multiple != 2)) {
|
||||
printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
|
||||
multiple);
|
||||
printk(KERN_ERR "ctxfi: The valid values for multiple are "
|
||||
"1 and 2, Value 2 is assumed.\n");
|
||||
multiple = 2;
|
||||
}
|
||||
err = ct_atc_create(card, pci, reference_rate, multiple,
|
||||
pci_id->driver_data, &atc);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
card->private_data = atc;
|
||||
|
||||
/* Create alsa devices supported by this card */
|
||||
err = ct_atc_create_alsa_devs(atc);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
strcpy(card->driver, "SB-XFi");
|
||||
strcpy(card->shortname, "Creative X-Fi");
|
||||
snprintf(card->longname, sizeof(card->longname), "%s %s %s",
|
||||
card->shortname, atc->chip_name, atc->model_name);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
dev++;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit ct_card_remove(struct pci_dev *pci)
|
||||
{
|
||||
snd_card_free(pci_get_drvdata(pci));
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
static struct pci_driver ct_driver = {
|
||||
.name = "SB-XFi",
|
||||
.id_table = ct_pci_dev_ids,
|
||||
.probe = ct_card_probe,
|
||||
.remove = __devexit_p(ct_card_remove),
|
||||
};
|
||||
|
||||
static int __init ct_card_init(void)
|
||||
{
|
||||
return pci_register_driver(&ct_driver);
|
||||
}
|
||||
|
||||
static void __exit ct_card_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ct_driver);
|
||||
}
|
||||
|
||||
module_init(ct_card_init)
|
||||
module_exit(ct_card_exit)
|
|
@ -9,15 +9,7 @@ snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
|
|||
snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
|
||||
snd-emu10k1x-objs := emu10k1x.o
|
||||
|
||||
#
|
||||
# this function returns:
|
||||
# "m" - CONFIG_SND_SEQUENCER is m
|
||||
# <empty string> - CONFIG_SND_SEQUENCER is undefined
|
||||
# otherwise parameter #1 value
|
||||
#
|
||||
sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
|
||||
obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o
|
||||
obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o
|
||||
obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
|
||||
|
|
|
@ -858,7 +858,6 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s
|
|||
}
|
||||
|
||||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
switch(device) {
|
||||
case 0:
|
||||
strcpy(pcm->name, "EMU10K1X Front");
|
||||
|
|
|
@ -1736,7 +1736,7 @@ static struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
|
|||
.buffer_bytes_max = (128*1024),
|
||||
.period_bytes_min = 1024,
|
||||
.period_bytes_max = (128*1024),
|
||||
.periods_min = 1,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
|
|
@ -139,6 +139,19 @@ config SND_HDA_CODEC_CONEXANT
|
|||
snd-hda-codec-conexant.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_CA0110
|
||||
bool "Build Creative CA0110-IBG codec support"
|
||||
depends on SND_HDA_INTEL
|
||||
default y
|
||||
help
|
||||
Say Y here to include Creative CA0110-IBG codec support in
|
||||
snd-hda-intel driver, found on some Creative X-Fi cards.
|
||||
|
||||
When the HD-audio driver is built as a module, the codec
|
||||
support code is also built as another module,
|
||||
snd-hda-codec-ca0110.
|
||||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_CODEC_CMEDIA
|
||||
bool "Build C-Media HD-audio codec support"
|
||||
default y
|
||||
|
|
|
@ -13,6 +13,7 @@ snd-hda-codec-analog-objs := patch_analog.o
|
|||
snd-hda-codec-idt-objs := patch_sigmatel.o
|
||||
snd-hda-codec-si3054-objs := patch_si3054.o
|
||||
snd-hda-codec-atihdmi-objs := patch_atihdmi.o
|
||||
snd-hda-codec-ca0110-objs := patch_ca0110.o
|
||||
snd-hda-codec-conexant-objs := patch_conexant.o
|
||||
snd-hda-codec-via-objs := patch_via.o
|
||||
snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
|
||||
|
@ -40,6 +41,9 @@ endif
|
|||
ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CA0110
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
|
||||
endif
|
||||
ifdef CONFIG_SND_HDA_CODEC_CONEXANT
|
||||
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
|
||||
endif
|
||||
|
|
|
@ -45,6 +45,46 @@ static void snd_hda_generate_beep(struct work_struct *work)
|
|||
AC_VERB_SET_BEEP_CONTROL, beep->tone);
|
||||
}
|
||||
|
||||
/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
|
||||
*
|
||||
* The tone frequency of beep generator on IDT/STAC codecs is
|
||||
* defined from the 8bit tone parameter, in Hz,
|
||||
* freq = 48000 * (257 - tone) / 1024
|
||||
* that is from 12kHz to 93.75kHz in step of 46.875 hz
|
||||
*/
|
||||
static int beep_linear_tone(struct hda_beep *beep, int hz)
|
||||
{
|
||||
hz *= 1000; /* fixed point */
|
||||
hz = hz - DIGBEEP_HZ_MIN;
|
||||
if (hz < 0)
|
||||
hz = 0; /* turn off PC beep*/
|
||||
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
|
||||
hz = 0xff;
|
||||
else {
|
||||
hz /= DIGBEEP_HZ_STEP;
|
||||
hz++;
|
||||
}
|
||||
return hz;
|
||||
}
|
||||
|
||||
/* HD-audio standard beep tone parameter calculation
|
||||
*
|
||||
* The tone frequency in Hz is calculated as
|
||||
* freq = 48000 / (tone * 4)
|
||||
* from 47Hz to 12kHz
|
||||
*/
|
||||
static int beep_standard_tone(struct hda_beep *beep, int hz)
|
||||
{
|
||||
if (hz <= 0)
|
||||
return 0; /* disabled */
|
||||
hz = 12000 / hz;
|
||||
if (hz > 0xff)
|
||||
return 0xff;
|
||||
if (hz <= 0)
|
||||
return 1;
|
||||
return hz;
|
||||
}
|
||||
|
||||
static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int hz)
|
||||
{
|
||||
|
@ -55,21 +95,14 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
|
|||
if (hz)
|
||||
hz = 1000;
|
||||
case SND_TONE:
|
||||
hz *= 1000; /* fixed point */
|
||||
hz = hz - DIGBEEP_HZ_MIN;
|
||||
if (hz < 0)
|
||||
hz = 0; /* turn off PC beep*/
|
||||
else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
|
||||
hz = 0xff;
|
||||
else {
|
||||
hz /= DIGBEEP_HZ_STEP;
|
||||
hz++;
|
||||
}
|
||||
if (beep->linear_tone)
|
||||
beep->tone = beep_linear_tone(beep, hz);
|
||||
else
|
||||
beep->tone = beep_standard_tone(beep, hz);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
beep->tone = hz;
|
||||
|
||||
/* schedule beep event */
|
||||
schedule_work(&beep->beep_work);
|
||||
|
|
|
@ -30,8 +30,9 @@ struct hda_beep {
|
|||
struct hda_codec *codec;
|
||||
char phys[32];
|
||||
int tone;
|
||||
int nid;
|
||||
int enabled;
|
||||
hda_nid_t nid;
|
||||
unsigned int enabled:1;
|
||||
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
|
||||
struct work_struct beep_work; /* scheduled task for beep event */
|
||||
};
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
|
|||
{ 0x1095, "Silicon Image" },
|
||||
{ 0x10de, "Nvidia" },
|
||||
{ 0x10ec, "Realtek" },
|
||||
{ 0x1102, "Creative" },
|
||||
{ 0x1106, "VIA" },
|
||||
{ 0x111d, "IDT" },
|
||||
{ 0x11c1, "LSI" },
|
||||
|
@ -157,6 +158,39 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
|
|||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send and receive a verb
|
||||
*/
|
||||
static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
|
||||
unsigned int *res)
|
||||
{
|
||||
struct hda_bus *bus = codec->bus;
|
||||
int err;
|
||||
|
||||
if (res)
|
||||
*res = -1;
|
||||
again:
|
||||
snd_hda_power_up(codec);
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
err = bus->ops.command(bus, cmd);
|
||||
if (!err && res)
|
||||
*res = bus->ops.get_response(bus);
|
||||
mutex_unlock(&bus->cmd_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
if (res && *res == -1 && bus->rirb_error) {
|
||||
if (bus->response_reset) {
|
||||
snd_printd("hda_codec: resetting BUS due to "
|
||||
"fatal communication error\n");
|
||||
bus->ops.bus_reset(bus);
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
/* clear reset-flag when the communication gets recovered */
|
||||
if (!err)
|
||||
bus->response_reset = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_codec_read - send a command and get the response
|
||||
* @codec: the HDA codec
|
||||
|
@ -173,18 +207,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
|
|||
int direct,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
struct hda_bus *bus = codec->bus;
|
||||
unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
|
||||
unsigned int res;
|
||||
|
||||
res = make_codec_cmd(codec, nid, direct, verb, parm);
|
||||
snd_hda_power_up(codec);
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
if (!bus->ops.command(bus, res))
|
||||
res = bus->ops.get_response(bus);
|
||||
else
|
||||
res = (unsigned int)-1;
|
||||
mutex_unlock(&bus->cmd_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
codec_exec_verb(codec, cmd, &res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_read);
|
||||
|
@ -204,17 +229,10 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read);
|
|||
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
|
||||
unsigned int verb, unsigned int parm)
|
||||
{
|
||||
struct hda_bus *bus = codec->bus;
|
||||
unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
|
||||
unsigned int res;
|
||||
int err;
|
||||
|
||||
res = make_codec_cmd(codec, nid, direct, verb, parm);
|
||||
snd_hda_power_up(codec);
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
err = bus->ops.command(bus, res);
|
||||
mutex_unlock(&bus->cmd_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
return err;
|
||||
return codec_exec_verb(codec, cmd,
|
||||
codec->bus->sync_write ? &res : NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_write);
|
||||
|
||||
|
@ -613,7 +631,10 @@ static int get_codec_name(struct hda_codec *codec)
|
|||
const struct hda_vendor_id *c;
|
||||
const char *vendor = NULL;
|
||||
u16 vendor_id = codec->vendor_id >> 16;
|
||||
char tmp[16], name[32];
|
||||
char tmp[16];
|
||||
|
||||
if (codec->vendor_name)
|
||||
goto get_chip_name;
|
||||
|
||||
for (c = hda_vendor_ids; c->id; c++) {
|
||||
if (c->id == vendor_id) {
|
||||
|
@ -625,14 +646,21 @@ static int get_codec_name(struct hda_codec *codec)
|
|||
sprintf(tmp, "Generic %04x", vendor_id);
|
||||
vendor = tmp;
|
||||
}
|
||||
codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
|
||||
if (!codec->vendor_name)
|
||||
return -ENOMEM;
|
||||
|
||||
get_chip_name:
|
||||
if (codec->chip_name)
|
||||
return 0;
|
||||
|
||||
if (codec->preset && codec->preset->name)
|
||||
snprintf(name, sizeof(name), "%s %s", vendor,
|
||||
codec->preset->name);
|
||||
else
|
||||
snprintf(name, sizeof(name), "%s ID %x", vendor,
|
||||
codec->vendor_id & 0xffff);
|
||||
codec->name = kstrdup(name, GFP_KERNEL);
|
||||
if (!codec->name)
|
||||
codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
|
||||
else {
|
||||
sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
|
||||
codec->chip_name = kstrdup(tmp, GFP_KERNEL);
|
||||
}
|
||||
if (!codec->chip_name)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
@ -838,7 +866,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
|
|||
module_put(codec->owner);
|
||||
free_hda_cache(&codec->amp_cache);
|
||||
free_hda_cache(&codec->cmd_cache);
|
||||
kfree(codec->name);
|
||||
kfree(codec->vendor_name);
|
||||
kfree(codec->chip_name);
|
||||
kfree(codec->modelname);
|
||||
kfree(codec->wcaps);
|
||||
kfree(codec);
|
||||
|
@ -979,15 +1008,16 @@ int snd_hda_codec_configure(struct hda_codec *codec)
|
|||
int err;
|
||||
|
||||
codec->preset = find_codec_preset(codec);
|
||||
if (!codec->name) {
|
||||
if (!codec->vendor_name || !codec->chip_name) {
|
||||
err = get_codec_name(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
/* audio codec should override the mixer name */
|
||||
if (codec->afg || !*codec->bus->card->mixername)
|
||||
strlcpy(codec->bus->card->mixername, codec->name,
|
||||
sizeof(codec->bus->card->mixername));
|
||||
snprintf(codec->bus->card->mixername,
|
||||
sizeof(codec->bus->card->mixername),
|
||||
"%s %s", codec->vendor_name, codec->chip_name);
|
||||
|
||||
if (is_generic_config(codec)) {
|
||||
err = snd_hda_parse_generic_codec(codec);
|
||||
|
@ -1055,6 +1085,8 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
|
|||
/* FIXME: more better hash key? */
|
||||
#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
|
||||
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
|
||||
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
|
||||
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
|
||||
#define INFO_AMP_CAPS (1<<0)
|
||||
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
|
||||
|
||||
|
@ -1145,19 +1177,32 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
|
||||
|
||||
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
|
||||
static unsigned int
|
||||
query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
|
||||
unsigned int (*func)(struct hda_codec *, hda_nid_t))
|
||||
{
|
||||
struct hda_amp_info *info;
|
||||
|
||||
info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
|
||||
info = get_alloc_amp_hash(codec, key);
|
||||
if (!info)
|
||||
return 0;
|
||||
if (!info->head.val) {
|
||||
info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
info->head.val |= INFO_AMP_CAPS;
|
||||
info->amp_caps = func(codec, nid);
|
||||
}
|
||||
return info->amp_caps;
|
||||
}
|
||||
|
||||
static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
|
||||
}
|
||||
|
||||
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
|
||||
read_pin_cap);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
|
||||
|
||||
/*
|
||||
|
@ -1432,6 +1477,8 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
|||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
id.index = idx;
|
||||
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
|
||||
return NULL;
|
||||
strcpy(id.name, name);
|
||||
return snd_ctl_find_id(codec->bus->card, &id);
|
||||
}
|
||||
|
@ -2242,28 +2289,22 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
|
|||
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
|
||||
int direct, unsigned int verb, unsigned int parm)
|
||||
{
|
||||
struct hda_bus *bus = codec->bus;
|
||||
unsigned int res;
|
||||
int err;
|
||||
int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
|
||||
res = make_codec_cmd(codec, nid, direct, verb, parm);
|
||||
snd_hda_power_up(codec);
|
||||
mutex_lock(&bus->cmd_mutex);
|
||||
err = bus->ops.command(bus, res);
|
||||
if (!err) {
|
||||
struct hda_cache_head *c;
|
||||
u32 key;
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
c = get_alloc_hash(&codec->cmd_cache, key);
|
||||
if (c)
|
||||
c->val = parm;
|
||||
}
|
||||
mutex_unlock(&bus->cmd_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
return err;
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* parm may contain the verb stuff for get/set amp */
|
||||
verb = verb | (parm >> 8);
|
||||
parm &= 0xff;
|
||||
key = build_cmd_cache_key(nid, verb);
|
||||
mutex_lock(&codec->bus->cmd_mutex);
|
||||
c = get_alloc_hash(&codec->cmd_cache, key);
|
||||
if (c)
|
||||
c->val = parm;
|
||||
mutex_unlock(&codec->bus->cmd_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
|
||||
|
||||
|
@ -2321,7 +2362,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
|||
if (wcaps & AC_WCAP_POWER) {
|
||||
unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
|
||||
AC_WCAP_TYPE_SHIFT;
|
||||
if (wid_type == AC_WID_PIN) {
|
||||
if (power_state == AC_PWRST_D3 &&
|
||||
wid_type == AC_WID_PIN) {
|
||||
unsigned int pincap;
|
||||
/*
|
||||
* don't power down the widget if it controls
|
||||
|
@ -2333,7 +2375,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
|||
nid, 0,
|
||||
AC_VERB_GET_EAPD_BTLENABLE, 0);
|
||||
eapd &= 0x02;
|
||||
if (power_state == AC_PWRST_D3 && eapd)
|
||||
if (eapd)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -2544,6 +2586,41 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
|||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
|
||||
|
||||
static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
if (nid != codec->afg &&
|
||||
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
|
||||
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
|
||||
if (!val || val == -1)
|
||||
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
|
||||
if (!val || val == -1)
|
||||
return 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
|
||||
get_pcm_param);
|
||||
}
|
||||
|
||||
static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
|
||||
if (!streams || streams == -1)
|
||||
streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
|
||||
if (!streams || streams == -1)
|
||||
return 0;
|
||||
return streams;
|
||||
}
|
||||
|
||||
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
|
||||
get_stream_param);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
|
||||
* @codec: the HDA codec
|
||||
|
@ -2562,15 +2639,8 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
|
|||
{
|
||||
unsigned int i, val, wcaps;
|
||||
|
||||
val = 0;
|
||||
wcaps = get_wcaps(codec, nid);
|
||||
if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
|
||||
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
|
||||
if (val == -1)
|
||||
return -EIO;
|
||||
}
|
||||
if (!val)
|
||||
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
|
||||
val = query_pcm_param(codec, nid);
|
||||
|
||||
if (ratesp) {
|
||||
u32 rates = 0;
|
||||
|
@ -2592,15 +2662,9 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
|
|||
u64 formats = 0;
|
||||
unsigned int streams, bps;
|
||||
|
||||
streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
|
||||
if (streams == -1)
|
||||
streams = query_stream_param(codec, nid);
|
||||
if (!streams)
|
||||
return -EIO;
|
||||
if (!streams) {
|
||||
streams = snd_hda_param_read(codec, codec->afg,
|
||||
AC_PAR_STREAM);
|
||||
if (streams == -1)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
bps = 0;
|
||||
if (streams & AC_SUPFMT_PCM) {
|
||||
|
@ -2674,17 +2738,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
|
|||
int i;
|
||||
unsigned int val = 0, rate, stream;
|
||||
|
||||
if (nid != codec->afg &&
|
||||
(get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
|
||||
val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
|
||||
if (val == -1)
|
||||
return 0;
|
||||
}
|
||||
if (!val) {
|
||||
val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
|
||||
if (val == -1)
|
||||
return 0;
|
||||
}
|
||||
val = query_pcm_param(codec, nid);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
rate = format & 0xff00;
|
||||
for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
|
||||
|
@ -2696,12 +2752,8 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
|
|||
if (i >= AC_PAR_PCM_RATE_BITS)
|
||||
return 0;
|
||||
|
||||
stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
|
||||
if (stream == -1)
|
||||
return 0;
|
||||
if (!stream && nid != codec->afg)
|
||||
stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
|
||||
if (!stream || stream == -1)
|
||||
stream = query_stream_param(codec, nid);
|
||||
if (!stream)
|
||||
return 0;
|
||||
|
||||
if (stream & AC_SUPFMT_PCM) {
|
||||
|
@ -3835,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
|
|||
/**
|
||||
* snd_hda_suspend - suspend the codecs
|
||||
* @bus: the HDA bus
|
||||
* @state: suspsend state
|
||||
*
|
||||
* Returns 0 if successful.
|
||||
*/
|
||||
int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
|
||||
int snd_hda_suspend(struct hda_bus *bus)
|
||||
{
|
||||
struct hda_codec *codec;
|
||||
|
||||
|
|
|
@ -574,6 +574,8 @@ struct hda_bus_ops {
|
|||
/* attach a PCM stream */
|
||||
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
|
||||
struct hda_pcm *pcm);
|
||||
/* reset bus for retry verb */
|
||||
void (*bus_reset)(struct hda_bus *bus);
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* notify power-up/down from codec to controller */
|
||||
void (*pm_notify)(struct hda_bus *bus);
|
||||
|
@ -622,7 +624,13 @@ struct hda_bus {
|
|||
|
||||
/* misc op flags */
|
||||
unsigned int needs_damn_long_delay :1;
|
||||
unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
|
||||
unsigned int sync_write:1; /* sync after verb write */
|
||||
/* status for codec/controller */
|
||||
unsigned int shutdown :1; /* being unloaded */
|
||||
unsigned int rirb_error:1; /* error in codec communication */
|
||||
unsigned int response_reset:1; /* controller was reset */
|
||||
unsigned int in_reset:1; /* during reset operation */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -747,7 +755,8 @@ struct hda_codec {
|
|||
/* detected preset */
|
||||
const struct hda_codec_preset *preset;
|
||||
struct module *owner;
|
||||
const char *name; /* codec name */
|
||||
const char *vendor_name; /* codec vendor name */
|
||||
const char *chip_name; /* codec chip name */
|
||||
const char *modelname; /* model name for preset */
|
||||
|
||||
/* set by patch */
|
||||
|
@ -905,7 +914,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
|
|||
* power management
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
|
||||
int snd_hda_suspend(struct hda_bus *bus);
|
||||
int snd_hda_resume(struct hda_bus *bus);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -242,7 +242,8 @@ CODEC_INFO_SHOW(subsystem_id);
|
|||
CODEC_INFO_SHOW(revision_id);
|
||||
CODEC_INFO_SHOW(afg);
|
||||
CODEC_INFO_SHOW(mfg);
|
||||
CODEC_INFO_STR_SHOW(name);
|
||||
CODEC_INFO_STR_SHOW(vendor_name);
|
||||
CODEC_INFO_STR_SHOW(chip_name);
|
||||
CODEC_INFO_STR_SHOW(modelname);
|
||||
|
||||
#define CODEC_INFO_STORE(type) \
|
||||
|
@ -275,7 +276,8 @@ static ssize_t type##_store(struct device *dev, \
|
|||
CODEC_INFO_STORE(vendor_id);
|
||||
CODEC_INFO_STORE(subsystem_id);
|
||||
CODEC_INFO_STORE(revision_id);
|
||||
CODEC_INFO_STR_STORE(name);
|
||||
CODEC_INFO_STR_STORE(vendor_name);
|
||||
CODEC_INFO_STR_STORE(chip_name);
|
||||
CODEC_INFO_STR_STORE(modelname);
|
||||
|
||||
#define CODEC_ACTION_STORE(type) \
|
||||
|
@ -499,7 +501,8 @@ static struct device_attribute codec_attrs[] = {
|
|||
CODEC_ATTR_RW(revision_id),
|
||||
CODEC_ATTR_RO(afg),
|
||||
CODEC_ATTR_RO(mfg),
|
||||
CODEC_ATTR_RW(name),
|
||||
CODEC_ATTR_RW(vendor_name),
|
||||
CODEC_ATTR_RW(chip_name),
|
||||
CODEC_ATTR_RW(modelname),
|
||||
CODEC_ATTR_RW(init_verbs),
|
||||
CODEC_ATTR_RW(hints),
|
||||
|
|
|
@ -128,21 +128,33 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
|
|||
"{ULI, M5461}}");
|
||||
MODULE_DESCRIPTION("Intel HDA driver");
|
||||
|
||||
#ifdef CONFIG_SND_VERBOSE_PRINTK
|
||||
#define SFX /* nop */
|
||||
#else
|
||||
#define SFX "hda-intel: "
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* registers
|
||||
*/
|
||||
#define ICH6_REG_GCAP 0x00
|
||||
#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
|
||||
#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
|
||||
#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
|
||||
#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
|
||||
#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
|
||||
#define ICH6_REG_VMIN 0x02
|
||||
#define ICH6_REG_VMAJ 0x03
|
||||
#define ICH6_REG_OUTPAY 0x04
|
||||
#define ICH6_REG_INPAY 0x06
|
||||
#define ICH6_REG_GCTL 0x08
|
||||
#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
|
||||
#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
|
||||
#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
|
||||
#define ICH6_REG_WAKEEN 0x0c
|
||||
#define ICH6_REG_STATESTS 0x0e
|
||||
#define ICH6_REG_GSTS 0x10
|
||||
#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
|
||||
#define ICH6_REG_INTCTL 0x20
|
||||
#define ICH6_REG_INTSTS 0x24
|
||||
#define ICH6_REG_WALCLK 0x30
|
||||
|
@ -150,17 +162,27 @@ MODULE_DESCRIPTION("Intel HDA driver");
|
|||
#define ICH6_REG_CORBLBASE 0x40
|
||||
#define ICH6_REG_CORBUBASE 0x44
|
||||
#define ICH6_REG_CORBWP 0x48
|
||||
#define ICH6_REG_CORBRP 0x4A
|
||||
#define ICH6_REG_CORBRP 0x4a
|
||||
#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
|
||||
#define ICH6_REG_CORBCTL 0x4c
|
||||
#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
|
||||
#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
|
||||
#define ICH6_REG_CORBSTS 0x4d
|
||||
#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
|
||||
#define ICH6_REG_CORBSIZE 0x4e
|
||||
|
||||
#define ICH6_REG_RIRBLBASE 0x50
|
||||
#define ICH6_REG_RIRBUBASE 0x54
|
||||
#define ICH6_REG_RIRBWP 0x58
|
||||
#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
|
||||
#define ICH6_REG_RINTCNT 0x5a
|
||||
#define ICH6_REG_RIRBCTL 0x5c
|
||||
#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
|
||||
#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
|
||||
#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
|
||||
#define ICH6_REG_RIRBSTS 0x5d
|
||||
#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
|
||||
#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
|
||||
#define ICH6_REG_RIRBSIZE 0x5e
|
||||
|
||||
#define ICH6_REG_IC 0x60
|
||||
|
@ -257,16 +279,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
|||
#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
|
||||
#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
|
||||
|
||||
/* GCTL unsolicited response enable bit */
|
||||
#define ICH6_GCTL_UREN (1<<8)
|
||||
|
||||
/* GCTL reset bit */
|
||||
#define ICH6_GCTL_RESET (1<<0)
|
||||
|
||||
/* CORB/RIRB control, read/write pointer */
|
||||
#define ICH6_RBCTL_DMA_EN 0x02 /* enable DMA */
|
||||
#define ICH6_RBCTL_IRQ_EN 0x01 /* enable IRQ */
|
||||
#define ICH6_RBRWP_CLR 0x8000 /* read/write pointer clear */
|
||||
/* below are so far hardcoded - should read registers in future */
|
||||
#define ICH6_MAX_CORB_ENTRIES 256
|
||||
#define ICH6_MAX_RIRB_ENTRIES 256
|
||||
|
@ -512,25 +524,25 @@ static void azx_init_cmd_io(struct azx *chip)
|
|||
/* set the corb write pointer to 0 */
|
||||
azx_writew(chip, CORBWP, 0);
|
||||
/* reset the corb hw read pointer */
|
||||
azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
|
||||
azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
|
||||
/* enable corb dma */
|
||||
azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
|
||||
azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
|
||||
|
||||
/* RIRB set up */
|
||||
chip->rirb.addr = chip->rb.addr + 2048;
|
||||
chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
|
||||
chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
|
||||
azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
|
||||
azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
|
||||
|
||||
/* set the rirb size to 256 entries (ULI requires explicitly) */
|
||||
azx_writeb(chip, RIRBSIZE, 0x02);
|
||||
/* reset the rirb hw write pointer */
|
||||
azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
|
||||
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
|
||||
/* set N=1, get RIRB response interrupt for new entry */
|
||||
azx_writew(chip, RINTCNT, 1);
|
||||
/* enable rirb dma and response irq */
|
||||
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
|
||||
chip->rirb.rp = chip->rirb.cmds = 0;
|
||||
}
|
||||
|
||||
static void azx_free_cmd_io(struct azx *chip)
|
||||
|
@ -606,6 +618,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
|||
}
|
||||
if (!chip->rirb.cmds) {
|
||||
smp_rmb();
|
||||
bus->rirb_error = 0;
|
||||
return chip->rirb.res; /* the last value */
|
||||
}
|
||||
if (time_after(jiffies, timeout))
|
||||
|
@ -619,19 +632,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
|||
}
|
||||
|
||||
if (chip->msi) {
|
||||
snd_printk(KERN_WARNING "hda_intel: No response from codec, "
|
||||
snd_printk(KERN_WARNING SFX "No response from codec, "
|
||||
"disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
|
||||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
pci_disable_msi(chip->pci);
|
||||
chip->msi = 0;
|
||||
if (azx_acquire_irq(chip, 1) < 0)
|
||||
if (azx_acquire_irq(chip, 1) < 0) {
|
||||
bus->rirb_error = 1;
|
||||
return -1;
|
||||
}
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (!chip->polling_mode) {
|
||||
snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
|
||||
snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
|
||||
"switching to polling mode: last cmd=0x%08x\n",
|
||||
chip->last_cmd);
|
||||
chip->polling_mode = 1;
|
||||
|
@ -646,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* a fatal communication error; need either to reset or to fallback
|
||||
* to the single_cmd mode
|
||||
*/
|
||||
bus->rirb_error = 1;
|
||||
if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
|
||||
bus->response_reset = 1;
|
||||
return -1; /* give a chance to retry */
|
||||
}
|
||||
|
||||
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
|
||||
"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 */
|
||||
chip->single_cmd = 1;
|
||||
bus->response_reset = 0;
|
||||
/* re-initialize CORB/RIRB */
|
||||
azx_free_cmd_io(chip);
|
||||
azx_init_cmd_io(chip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -667,12 +691,34 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
|
|||
* I left the codes, however, for debugging/testing purposes.
|
||||
*/
|
||||
|
||||
/* receive a response */
|
||||
static int azx_single_wait_for_response(struct azx *chip)
|
||||
{
|
||||
int timeout = 50;
|
||||
|
||||
while (timeout--) {
|
||||
/* check IRV busy bit */
|
||||
if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
|
||||
/* reuse rirb.res as the response return value */
|
||||
chip->rirb.res = azx_readl(chip, IR);
|
||||
return 0;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
if (printk_ratelimit())
|
||||
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
|
||||
azx_readw(chip, IRS));
|
||||
chip->rirb.res = -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* send a command */
|
||||
static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
int timeout = 50;
|
||||
|
||||
bus->rirb_error = 0;
|
||||
while (timeout--) {
|
||||
/* check ICB busy bit */
|
||||
if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
|
||||
|
@ -682,7 +728,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
|||
azx_writel(chip, IC, val);
|
||||
azx_writew(chip, IRS, azx_readw(chip, IRS) |
|
||||
ICH6_IRS_BUSY);
|
||||
return 0;
|
||||
return azx_single_wait_for_response(chip);
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
@ -696,18 +742,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
|
|||
static unsigned int azx_single_get_response(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
int timeout = 50;
|
||||
|
||||
while (timeout--) {
|
||||
/* check IRV busy bit */
|
||||
if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
|
||||
return azx_readl(chip, IR);
|
||||
udelay(1);
|
||||
}
|
||||
if (printk_ratelimit())
|
||||
snd_printd(SFX "get_response timeout: IRS=0x%x\n",
|
||||
azx_readw(chip, IRS));
|
||||
return (unsigned int)-1;
|
||||
return chip->rirb.res;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -775,17 +810,17 @@ static int azx_reset(struct azx *chip)
|
|||
|
||||
/* check to see if controller is ready */
|
||||
if (!azx_readb(chip, GCTL)) {
|
||||
snd_printd("azx_reset: controller not ready!\n");
|
||||
snd_printd(SFX "azx_reset: controller not ready!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Accept unsolicited responses */
|
||||
azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
|
||||
azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
|
||||
|
||||
/* detect codecs */
|
||||
if (!chip->codec_mask) {
|
||||
chip->codec_mask = azx_readw(chip, STATESTS);
|
||||
snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
|
||||
snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -895,8 +930,7 @@ static void azx_init_chip(struct azx *chip)
|
|||
azx_int_enable(chip);
|
||||
|
||||
/* initialize the codec command I/O */
|
||||
if (!chip->single_cmd)
|
||||
azx_init_cmd_io(chip);
|
||||
azx_init_cmd_io(chip);
|
||||
|
||||
/* program the position buffer */
|
||||
azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
|
||||
|
@ -953,12 +987,12 @@ static void azx_init_pci(struct azx *chip)
|
|||
case AZX_DRIVER_SCH:
|
||||
pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
|
||||
if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
|
||||
pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \
|
||||
pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
|
||||
snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
|
||||
pci_read_config_word(chip->pci,
|
||||
INTEL_SCH_HDA_DEVC, &snoop);
|
||||
snd_printdd("HDA snoop disabled, enabling ... %s\n",\
|
||||
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \
|
||||
snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
|
||||
(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
|
||||
? "Failed" : "OK");
|
||||
}
|
||||
break;
|
||||
|
@ -1012,7 +1046,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
|
|||
/* clear rirb int */
|
||||
status = azx_readb(chip, RIRBSTS);
|
||||
if (status & RIRB_INT_MASK) {
|
||||
if (!chip->single_cmd && (status & RIRB_INT_RESPONSE))
|
||||
if (status & RIRB_INT_RESPONSE)
|
||||
azx_update_rirb(chip);
|
||||
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
|
||||
}
|
||||
|
@ -1098,7 +1132,7 @@ static int azx_setup_periods(struct azx *chip,
|
|||
pos_align;
|
||||
pos_adj = frames_to_bytes(runtime, pos_adj);
|
||||
if (pos_adj >= period_bytes) {
|
||||
snd_printk(KERN_WARNING "Too big adjustment %d\n",
|
||||
snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
|
||||
bdl_pos_adj[chip->dev_index]);
|
||||
pos_adj = 0;
|
||||
} else {
|
||||
|
@ -1122,7 +1156,7 @@ static int azx_setup_periods(struct azx *chip,
|
|||
return 0;
|
||||
|
||||
error:
|
||||
snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
|
||||
snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
|
||||
azx_dev->bufsize, period_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1215,7 +1249,7 @@ static int probe_codec(struct azx *chip, int addr)
|
|||
chip->probing = 0;
|
||||
if (res == -1)
|
||||
return -EIO;
|
||||
snd_printdd("hda_intel: codec #%d probed OK\n", addr);
|
||||
snd_printdd(SFX "codec #%d probed OK\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1223,6 +1257,26 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
|
|||
struct hda_pcm *cpcm);
|
||||
static void azx_stop_chip(struct azx *chip);
|
||||
|
||||
static void azx_bus_reset(struct hda_bus *bus)
|
||||
{
|
||||
struct azx *chip = bus->private_data;
|
||||
|
||||
bus->in_reset = 1;
|
||||
azx_stop_chip(chip);
|
||||
azx_init_chip(chip);
|
||||
#ifdef CONFIG_PM
|
||||
if (chip->initialized) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AZX_MAX_PCMS; i++)
|
||||
snd_pcm_suspend_all(chip->pcm[i]);
|
||||
snd_hda_suspend(chip->bus);
|
||||
snd_hda_resume(chip->bus);
|
||||
}
|
||||
#endif
|
||||
bus->in_reset = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Codec initialization
|
||||
*/
|
||||
|
@ -1246,6 +1300,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
|||
bus_temp.ops.command = azx_send_cmd;
|
||||
bus_temp.ops.get_response = azx_get_response;
|
||||
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
|
||||
bus_temp.ops.bus_reset = azx_bus_reset;
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
bus_temp.power_save = &power_save;
|
||||
bus_temp.ops.pm_notify = azx_power_notify;
|
||||
|
@ -1270,8 +1325,8 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
|
|||
/* Some BIOSen give you wrong codec addresses
|
||||
* that don't exist
|
||||
*/
|
||||
snd_printk(KERN_WARNING
|
||||
"hda_intel: Codec #%d probe error; "
|
||||
snd_printk(KERN_WARNING SFX
|
||||
"Codec #%d probe error; "
|
||||
"disabling it...\n", c);
|
||||
chip->codec_mask &= ~(1 << c);
|
||||
/* More badly, accessing to a non-existing
|
||||
|
@ -1487,7 +1542,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
bufsize = snd_pcm_lib_buffer_bytes(substream);
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
|
||||
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
|
||||
snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
|
||||
bufsize, format_val);
|
||||
|
||||
if (bufsize != azx_dev->bufsize ||
|
||||
|
@ -1830,7 +1885,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
|
|||
&pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
strcpy(pcm->name, cpcm->name);
|
||||
strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
|
||||
apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
|
||||
if (apcm == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -1973,7 +2028,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
for (i = 0; i < AZX_MAX_PCMS; i++)
|
||||
snd_pcm_suspend_all(chip->pcm[i]);
|
||||
if (chip->initialized)
|
||||
snd_hda_suspend(chip->bus, state);
|
||||
snd_hda_suspend(chip->bus);
|
||||
azx_stop_chip(chip);
|
||||
if (chip->irq >= 0) {
|
||||
free_irq(chip->irq, chip);
|
||||
|
@ -2265,14 +2320,14 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
synchronize_irq(chip->irq);
|
||||
|
||||
gcap = azx_readw(chip, GCAP);
|
||||
snd_printdd("chipset global capabilities = 0x%x\n", gcap);
|
||||
snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
|
||||
|
||||
/* ATI chips seems buggy about 64bit DMA addresses */
|
||||
if (chip->driver_type == AZX_DRIVER_ATI)
|
||||
gcap &= ~0x01;
|
||||
gcap &= ~ICH6_GCAP_64OK;
|
||||
|
||||
/* allow 64bit DMA address if supported by H/W */
|
||||
if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
|
||||
if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
|
||||
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
|
||||
else {
|
||||
pci_set_dma_mask(pci, DMA_BIT_MASK(32));
|
||||
|
@ -2309,7 +2364,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
|
||||
GFP_KERNEL);
|
||||
if (!chip->azx_dev) {
|
||||
snd_printk(KERN_ERR "cannot malloc azx_dev\n");
|
||||
snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
|
@ -2332,11 +2387,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
goto errout;
|
||||
}
|
||||
/* allocate CORB/RIRB */
|
||||
if (!chip->single_cmd) {
|
||||
err = azx_alloc_cmd_io(chip);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
}
|
||||
err = azx_alloc_cmd_io(chip);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
/* initialize streams */
|
||||
azx_init_stream(chip);
|
||||
|
@ -2359,9 +2412,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
}
|
||||
|
||||
strcpy(card->driver, "HDA-Intel");
|
||||
strcpy(card->shortname, driver_short_names[chip->driver_type]);
|
||||
sprintf(card->longname, "%s at 0x%lx irq %i",
|
||||
card->shortname, chip->addr, chip->irq);
|
||||
strlcpy(card->shortname, driver_short_names[chip->driver_type],
|
||||
sizeof(card->shortname));
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s at 0x%lx irq %i",
|
||||
card->shortname, chip->addr, chip->irq);
|
||||
|
||||
*rchip = chip;
|
||||
return 0;
|
||||
|
@ -2514,6 +2569,20 @@ static struct pci_device_id azx_ids[] = {
|
|||
{ PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
/* Teradici */
|
||||
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
|
||||
/* Creative X-Fi (CA0110-IBG) */
|
||||
#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
|
||||
/* the following entry conflicts with snd-ctxfi driver,
|
||||
* as ctxfi driver mutates from HD-audio to native mode with
|
||||
* a special command sequence.
|
||||
*/
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
.class_mask = 0xffffff,
|
||||
.driver_data = AZX_DRIVER_GENERIC },
|
||||
#else
|
||||
/* this entry seems still valid -- i.e. without emu20kx chip */
|
||||
{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
|
||||
#endif
|
||||
/* AMD Generic, PCI class code and Vendor ID for HD Audio */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
|
||||
|
|
|
@ -466,8 +466,12 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
hda_nid_t nid;
|
||||
int i, nodes;
|
||||
|
||||
snd_iprintf(buffer, "Codec: %s\n",
|
||||
codec->name ? codec->name : "Not Set");
|
||||
snd_iprintf(buffer, "Codec: ");
|
||||
if (codec->vendor_name && codec->chip_name)
|
||||
snd_iprintf(buffer, "%s %s\n",
|
||||
codec->vendor_name, codec->chip_name);
|
||||
else
|
||||
snd_iprintf(buffer, "Not Set\n");
|
||||
snd_iprintf(buffer, "Address: %d\n", codec->addr);
|
||||
snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
|
||||
snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
|
||||
|
|
573
sound/pci/hda/patch_ca0110.c
Normal file
573
sound/pci/hda/patch_ca0110.c
Normal file
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
* HD audio interface patch for Creative X-Fi CA0110-IBG chip
|
||||
*
|
||||
* Copyright (c) 2008 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This driver 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/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
struct ca0110_spec {
|
||||
struct auto_pin_cfg autocfg;
|
||||
struct hda_multi_out multiout;
|
||||
hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
|
||||
hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
|
||||
hda_nid_t hp_dac;
|
||||
hda_nid_t input_pins[AUTO_PIN_LAST];
|
||||
hda_nid_t adcs[AUTO_PIN_LAST];
|
||||
hda_nid_t dig_out;
|
||||
hda_nid_t dig_in;
|
||||
unsigned int num_inputs;
|
||||
const char *input_labels[AUTO_PIN_LAST];
|
||||
struct hda_pcm pcm_rec[2]; /* PCM information */
|
||||
};
|
||||
|
||||
/*
|
||||
* PCM callbacks
|
||||
*/
|
||||
static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
|
||||
stream_tag, format, substream);
|
||||
}
|
||||
|
||||
static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
/*
|
||||
* Digital out
|
||||
*/
|
||||
static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||
format, substream);
|
||||
}
|
||||
|
||||
/*
|
||||
* Analog capture
|
||||
*/
|
||||
static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
|
||||
stream_tag, 0, format);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
static char *dirstr[2] = { "Playback", "Capture" };
|
||||
|
||||
static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
|
||||
int chan, int dir)
|
||||
{
|
||||
char namestr[44];
|
||||
int type = dir ? HDA_INPUT : HDA_OUTPUT;
|
||||
struct snd_kcontrol_new knew =
|
||||
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
|
||||
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
|
||||
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
}
|
||||
|
||||
static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
|
||||
int chan, int dir)
|
||||
{
|
||||
char namestr[44];
|
||||
int type = dir ? HDA_INPUT : HDA_OUTPUT;
|
||||
struct snd_kcontrol_new knew =
|
||||
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
|
||||
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
|
||||
return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
|
||||
}
|
||||
|
||||
#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
|
||||
#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
|
||||
#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
|
||||
#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
|
||||
#define add_mono_switch(codec, nid, pfx, chan) \
|
||||
_add_switch(codec, nid, pfx, chan, 0)
|
||||
#define add_mono_volume(codec, nid, pfx, chan) \
|
||||
_add_volume(codec, nid, pfx, chan, 0)
|
||||
|
||||
static int ca0110_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
static char *prefix[AUTO_CFG_MAX_OUTS] = {
|
||||
"Front", "Surround", NULL, "Side", "Multi"
|
||||
};
|
||||
hda_nid_t mutenid;
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < spec->multiout.num_dacs; i++) {
|
||||
if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
|
||||
mutenid = spec->out_pins[i];
|
||||
else
|
||||
mutenid = spec->multiout.dac_nids[i];
|
||||
if (!prefix[i]) {
|
||||
err = add_mono_switch(codec, mutenid,
|
||||
"Center", 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_mono_switch(codec, mutenid,
|
||||
"LFE", 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
|
||||
"Center", 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_mono_volume(codec, spec->multiout.dac_nids[i],
|
||||
"LFE", 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else {
|
||||
err = add_out_switch(codec, mutenid,
|
||||
prefix[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_out_volume(codec, spec->multiout.dac_nids[i],
|
||||
prefix[i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (cfg->hp_outs) {
|
||||
if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
|
||||
mutenid = cfg->hp_pins[0];
|
||||
else
|
||||
mutenid = spec->multiout.dac_nids[i];
|
||||
|
||||
err = add_out_switch(codec, mutenid, "Headphone");
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (spec->hp_dac) {
|
||||
err = add_out_volume(codec, spec->hp_dac, "Headphone");
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < spec->num_inputs; i++) {
|
||||
const char *label = spec->input_labels[i];
|
||||
if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
|
||||
mutenid = spec->input_pins[i];
|
||||
else
|
||||
mutenid = spec->adcs[i];
|
||||
err = add_in_switch(codec, mutenid, label);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_in_volume(codec, spec->adcs[i], label);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spec->dig_out) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec->multiout.share_spdif = 1;
|
||||
}
|
||||
if (spec->dig_in) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_in_volume(codec, spec->dig_in, "IEC958");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static struct hda_pcm_stream ca0110_pcm_analog_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.ops = {
|
||||
.open = ca0110_playback_pcm_open,
|
||||
.prepare = ca0110_playback_pcm_prepare,
|
||||
.cleanup = ca0110_playback_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_pcm_stream ca0110_pcm_analog_capture = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.ops = {
|
||||
.prepare = ca0110_capture_pcm_prepare,
|
||||
.cleanup = ca0110_capture_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_pcm_stream ca0110_pcm_digital_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.ops = {
|
||||
.open = ca0110_dig_playback_pcm_open,
|
||||
.close = ca0110_dig_playback_pcm_close,
|
||||
.prepare = ca0110_dig_playback_pcm_prepare
|
||||
},
|
||||
};
|
||||
|
||||
static struct hda_pcm_stream ca0110_pcm_digital_capture = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
};
|
||||
|
||||
static int ca0110_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
|
||||
codec->pcm_info = info;
|
||||
codec->num_pcms = 0;
|
||||
|
||||
info->name = "CA0110 Analog";
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
|
||||
spec->multiout.max_channels;
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
|
||||
codec->num_pcms++;
|
||||
|
||||
if (!spec->dig_out && !spec->dig_in)
|
||||
return 0;
|
||||
|
||||
info++;
|
||||
info->name = "CA0110 Digital";
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
if (spec->dig_out) {
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
|
||||
ca0110_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
|
||||
}
|
||||
if (spec->dig_in) {
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE] =
|
||||
ca0110_pcm_digital_capture;
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
|
||||
}
|
||||
codec->num_pcms++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
|
||||
{
|
||||
if (pin) {
|
||||
snd_hda_codec_write(codec, pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
|
||||
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_UNMUTE);
|
||||
}
|
||||
if (dac)
|
||||
snd_hda_codec_write(codec, dac, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
|
||||
}
|
||||
|
||||
static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
|
||||
{
|
||||
if (pin) {
|
||||
snd_hda_codec_write(codec, pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
|
||||
if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
|
||||
snd_hda_codec_write(codec, pin, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_IN_UNMUTE(0));
|
||||
}
|
||||
if (adc)
|
||||
snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_IN_UNMUTE(0));
|
||||
}
|
||||
|
||||
static int ca0110_init(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->multiout.num_dacs; i++)
|
||||
init_output(codec, spec->out_pins[i],
|
||||
spec->multiout.dac_nids[i]);
|
||||
init_output(codec, cfg->hp_pins[0], spec->hp_dac);
|
||||
init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
|
||||
|
||||
for (i = 0; i < spec->num_inputs; i++)
|
||||
init_input(codec, spec->input_pins[i], spec->adcs[i]);
|
||||
init_input(codec, cfg->dig_in_pin, spec->dig_in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ca0110_free(struct hda_codec *codec)
|
||||
{
|
||||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
static struct hda_codec_ops ca0110_patch_ops = {
|
||||
.build_controls = ca0110_build_controls,
|
||||
.build_pcms = ca0110_build_pcms,
|
||||
.init = ca0110_init,
|
||||
.free = ca0110_free,
|
||||
};
|
||||
|
||||
|
||||
static void parse_line_outs(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i, n;
|
||||
unsigned int def_conf;
|
||||
hda_nid_t nid;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < cfg->line_outs; i++) {
|
||||
nid = cfg->line_out_pins[i];
|
||||
def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
||||
if (!def_conf)
|
||||
continue; /* invalid pin */
|
||||
if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
|
||||
continue;
|
||||
spec->out_pins[n++] = nid;
|
||||
}
|
||||
spec->multiout.dac_nids = spec->dacs;
|
||||
spec->multiout.num_dacs = n;
|
||||
spec->multiout.max_channels = n * 2;
|
||||
}
|
||||
|
||||
static void parse_hp_out(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i;
|
||||
unsigned int def_conf;
|
||||
hda_nid_t nid, dac;
|
||||
|
||||
if (!cfg->hp_outs)
|
||||
return;
|
||||
nid = cfg->hp_pins[0];
|
||||
def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
||||
if (!def_conf) {
|
||||
cfg->hp_outs = 0;
|
||||
return;
|
||||
}
|
||||
if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
|
||||
return;
|
||||
|
||||
for (i = 0; i < cfg->line_outs; i++)
|
||||
if (dac == spec->dacs[i])
|
||||
break;
|
||||
if (i >= cfg->line_outs) {
|
||||
spec->hp_dac = dac;
|
||||
spec->multiout.hp_nid = dac;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_input(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
hda_nid_t nid, pin;
|
||||
int n, i, j;
|
||||
|
||||
n = 0;
|
||||
nid = codec->start_nid;
|
||||
for (i = 0; i < codec->num_nodes; i++, nid++) {
|
||||
unsigned int wcaps = get_wcaps(codec, nid);
|
||||
unsigned int type = (wcaps & AC_WCAP_TYPE) >>
|
||||
AC_WCAP_TYPE_SHIFT;
|
||||
if (type != AC_WID_AUD_IN)
|
||||
continue;
|
||||
if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
|
||||
continue;
|
||||
if (pin == cfg->dig_in_pin) {
|
||||
spec->dig_in = nid;
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < AUTO_PIN_LAST; j++)
|
||||
if (cfg->input_pins[j] == pin)
|
||||
break;
|
||||
if (j >= AUTO_PIN_LAST)
|
||||
continue;
|
||||
spec->input_pins[n] = pin;
|
||||
spec->input_labels[n] = auto_pin_cfg_labels[j];
|
||||
spec->adcs[n] = nid;
|
||||
n++;
|
||||
}
|
||||
spec->num_inputs = n;
|
||||
}
|
||||
|
||||
static void parse_digital(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
|
||||
if (cfg->dig_outs &&
|
||||
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
|
||||
&spec->dig_out, 1) == 1)
|
||||
spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
|
||||
}
|
||||
|
||||
static int ca0110_parse_auto_config(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec = codec->spec;
|
||||
int err;
|
||||
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
parse_line_outs(codec);
|
||||
parse_hp_out(codec);
|
||||
parse_digital(codec);
|
||||
parse_input(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int patch_ca0110(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0110_spec *spec;
|
||||
int err;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
codec->spec = spec;
|
||||
|
||||
codec->bus->needs_damn_long_delay = 1;
|
||||
|
||||
err = ca0110_parse_auto_config(codec);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
codec->patch_ops = ca0110_patch_ops;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(codec->spec);
|
||||
codec->spec = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* patch entries
|
||||
*/
|
||||
static struct hda_codec_preset snd_hda_preset_ca0110[] = {
|
||||
{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
|
||||
{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
|
||||
{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000a");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000b");
|
||||
MODULE_ALIAS("snd-hda-codec-id:1102000d");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
|
||||
|
||||
static struct hda_codec_preset_list ca0110_list = {
|
||||
.preset = snd_hda_preset_ca0110,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init patch_ca0110_init(void)
|
||||
{
|
||||
return snd_hda_add_codec_preset(&ca0110_list);
|
||||
}
|
||||
|
||||
static void __exit patch_ca0110_exit(void)
|
||||
{
|
||||
snd_hda_delete_codec_preset(&ca0110_list);
|
||||
}
|
||||
|
||||
module_init(patch_ca0110_init)
|
||||
module_exit(patch_ca0110_exit)
|
|
@ -35,9 +35,28 @@ struct nvhdmi_spec {
|
|||
struct hda_pcm pcm_rec;
|
||||
};
|
||||
|
||||
#define Nv_VERB_SET_Channel_Allocation 0xF79
|
||||
#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
|
||||
#define Nv_VERB_SET_Audio_Protection_On 0xF98
|
||||
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
|
||||
|
||||
#define Nv_Master_Convert_nid 0x04
|
||||
#define Nv_Master_Pin_nid 0x05
|
||||
|
||||
static hda_nid_t nvhdmi_convert_nids[4] = {
|
||||
/*front, rear, clfe, rear_surr */
|
||||
0x6, 0x8, 0xa, 0xc,
|
||||
};
|
||||
|
||||
static struct hda_verb nvhdmi_basic_init[] = {
|
||||
/* set audio protect on */
|
||||
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
||||
/* enable digital output on pin widget */
|
||||
{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
||||
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -66,48 +85,205 @@ static int nvhdmi_init(struct hda_codec *codec)
|
|||
* Digital out
|
||||
*/
|
||||
static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_open(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid,
|
||||
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
}
|
||||
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
int chs;
|
||||
unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
|
||||
int i;
|
||||
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
|
||||
chs = substream->runtime->channels;
|
||||
chan = chs ? (chs - 1) : 1;
|
||||
|
||||
switch (chs) {
|
||||
default:
|
||||
case 0:
|
||||
case 2:
|
||||
chanmask = 0x00;
|
||||
break;
|
||||
case 4:
|
||||
chanmask = 0x08;
|
||||
break;
|
||||
case 6:
|
||||
chanmask = 0x0b;
|
||||
break;
|
||||
case 8:
|
||||
chanmask = 0x13;
|
||||
break;
|
||||
}
|
||||
dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
|
||||
dataDCC2 = 0x2;
|
||||
|
||||
/* set the Audio InforFrame Channel Allocation */
|
||||
snd_hda_codec_write(codec, 0x1, 0,
|
||||
Nv_VERB_SET_Channel_Allocation, chanmask);
|
||||
|
||||
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
|
||||
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
|
||||
AC_VERB_SET_STREAM_FORMAT, format);
|
||||
|
||||
/* turn on again (if needed) */
|
||||
/* enable and set the channel status audio/data flag */
|
||||
if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & 0xff);
|
||||
snd_hda_codec_write(codec,
|
||||
Nv_Master_Convert_nid,
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (chs == 2)
|
||||
channel_id = 0;
|
||||
else
|
||||
channel_id = i * 2;
|
||||
|
||||
/* turn off SPDIF once;
|
||||
*otherwise the IEC958 bits won't be updated
|
||||
*/
|
||||
if (codec->spdif_status_reset &&
|
||||
(codec->spdif_ctls & AC_DIG1_ENABLE))
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||
/* set the stream id */
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
0,
|
||||
AC_VERB_SET_CHANNEL_STREAMID,
|
||||
(stream_tag << 4) | channel_id);
|
||||
/* set the stream format */
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
0,
|
||||
AC_VERB_SET_STREAM_FORMAT,
|
||||
format);
|
||||
/* turn on again (if needed) */
|
||||
/* enable and set the channel status audio/data flag */
|
||||
if (codec->spdif_status_reset &&
|
||||
(codec->spdif_ctls & AC_DIG1_ENABLE)) {
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & 0xff);
|
||||
snd_hda_codec_write(codec,
|
||||
nvhdmi_convert_nids[i],
|
||||
0,
|
||||
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
|
||||
}
|
||||
}
|
||||
|
||||
/* set the Audio Info Frame Checksum */
|
||||
snd_hda_codec_write(codec, 0x1, 0,
|
||||
Nv_VERB_SET_Info_Frame_Checksum,
|
||||
(0x71 - chan - chanmask));
|
||||
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
|
||||
format, substream);
|
||||
format, substream);
|
||||
}
|
||||
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.nid = 0x4, /* NID to query formats and rates and setup streams */
|
||||
.channels_max = 8,
|
||||
.nid = Nv_Master_Convert_nid,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.maxbps = 16,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.ops = {
|
||||
.open = nvhdmi_dig_playback_pcm_open,
|
||||
.close = nvhdmi_dig_playback_pcm_close,
|
||||
.prepare = nvhdmi_dig_playback_pcm_prepare
|
||||
.close = nvhdmi_dig_playback_pcm_close_8ch,
|
||||
.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
|
||||
},
|
||||
};
|
||||
|
||||
static int nvhdmi_build_pcms(struct hda_codec *codec)
|
||||
static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.nid = Nv_Master_Convert_nid,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.maxbps = 16,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.ops = {
|
||||
.open = nvhdmi_dig_playback_pcm_open,
|
||||
.close = nvhdmi_dig_playback_pcm_close_2ch,
|
||||
.prepare = nvhdmi_dig_playback_pcm_prepare_2ch
|
||||
},
|
||||
};
|
||||
|
||||
static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
|
@ -117,7 +293,24 @@ static int nvhdmi_build_pcms(struct hda_codec *codec)
|
|||
|
||||
info->name = "NVIDIA HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
||||
= nvhdmi_pcm_digital_playback_8ch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = &spec->pcm_rec;
|
||||
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
||||
info->name = "NVIDIA HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK]
|
||||
= nvhdmi_pcm_digital_playback_2ch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -127,14 +320,21 @@ static void nvhdmi_free(struct hda_codec *codec)
|
|||
kfree(codec->spec);
|
||||
}
|
||||
|
||||
static struct hda_codec_ops nvhdmi_patch_ops = {
|
||||
static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
|
||||
.build_controls = nvhdmi_build_controls,
|
||||
.build_pcms = nvhdmi_build_pcms,
|
||||
.build_pcms = nvhdmi_build_pcms_8ch,
|
||||
.init = nvhdmi_init,
|
||||
.free = nvhdmi_free,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi(struct hda_codec *codec)
|
||||
static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
|
||||
.build_controls = nvhdmi_build_controls,
|
||||
.build_pcms = nvhdmi_build_pcms_2ch,
|
||||
.init = nvhdmi_init,
|
||||
.free = nvhdmi_free,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi_8ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec;
|
||||
|
||||
|
@ -144,13 +344,30 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
|||
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
|
||||
* seems to be unused in pure-digital
|
||||
* case. */
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
|
||||
|
||||
codec->patch_ops = nvhdmi_patch_ops;
|
||||
codec->patch_ops = nvhdmi_patch_ops_8ch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||
{
|
||||
struct nvhdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
|
||||
|
||||
codec->patch_ops = nvhdmi_patch_ops_2ch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -159,11 +376,11 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
|||
* patch entries
|
||||
*/
|
||||
static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
|
||||
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi },
|
||||
{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -100,6 +100,7 @@ enum {
|
|||
STAC_HP_M4,
|
||||
STAC_HP_DV5,
|
||||
STAC_HP_HDX,
|
||||
STAC_HP_DV4_1222NR,
|
||||
STAC_92HD71BXX_MODELS
|
||||
};
|
||||
|
||||
|
@ -193,6 +194,7 @@ struct sigmatel_spec {
|
|||
unsigned int gpio_dir;
|
||||
unsigned int gpio_data;
|
||||
unsigned int gpio_mute;
|
||||
unsigned int gpio_led;
|
||||
|
||||
/* stream */
|
||||
unsigned int stream_delay;
|
||||
|
@ -634,6 +636,40 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int stac92xx_vref_set(struct hda_codec *codec,
|
||||
hda_nid_t nid, unsigned int new_vref)
|
||||
{
|
||||
unsigned int error;
|
||||
unsigned int pincfg;
|
||||
pincfg = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
|
||||
pincfg &= 0xff;
|
||||
pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
|
||||
pincfg |= new_vref;
|
||||
|
||||
if (new_vref == AC_PINCTL_VREF_HIZ)
|
||||
pincfg |= AC_PINCTL_OUT_EN;
|
||||
else
|
||||
pincfg |= AC_PINCTL_IN_EN;
|
||||
|
||||
error = snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
|
||||
if (error < 0)
|
||||
return error;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int vref;
|
||||
vref = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
vref &= AC_PINCTL_VREFEN;
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
|
@ -995,6 +1031,17 @@ static struct hda_verb stac9205_core_init[] = {
|
|||
.private_value = verb_read | (verb_write << 16), \
|
||||
}
|
||||
|
||||
#define DC_BIAS(xname, idx, nid) \
|
||||
{ \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.index = idx, \
|
||||
.info = stac92xx_dc_bias_info, \
|
||||
.get = stac92xx_dc_bias_get, \
|
||||
.put = stac92xx_dc_bias_put, \
|
||||
.private_value = nid, \
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new stac9200_mixer[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
|
||||
|
@ -1543,6 +1590,8 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
|
|||
/* SigmaTel reference board */
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
|
||||
"DFI LanParty", STAC_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
|
||||
"SigmaTel",STAC_9205_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
|
||||
"DFI LanParty", STAC_REF),
|
||||
/* Dell laptops have BIOS problem */
|
||||
|
@ -1837,6 +1886,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
|
|||
[STAC_HP_M4] = NULL,
|
||||
[STAC_HP_DV5] = NULL,
|
||||
[STAC_HP_HDX] = NULL,
|
||||
[STAC_HP_DV4_1222NR] = NULL,
|
||||
};
|
||||
|
||||
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
|
||||
|
@ -1848,6 +1898,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
|
|||
[STAC_HP_M4] = "hp-m4",
|
||||
[STAC_HP_DV5] = "hp-dv5",
|
||||
[STAC_HP_HDX] = "hp-hdx",
|
||||
[STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
|
||||
|
@ -1856,6 +1907,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
|
|||
"DFI LanParty", STAC_92HD71BXX_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
|
||||
"DFI LanParty", STAC_92HD71BXX_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
|
||||
"HP dv4-1222nr", STAC_HP_DV4_1222NR),
|
||||
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
|
||||
"HP", STAC_HP_DV5),
|
||||
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
|
||||
|
@ -2545,7 +2598,8 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
|
||||
static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
|
||||
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
|
||||
|
@ -2599,15 +2653,108 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
|
||||
static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
int i;
|
||||
static char *texts[] = {
|
||||
"Mic In", "Line In", "Line Out"
|
||||
};
|
||||
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
|
||||
if (nid == spec->mic_switch || nid == spec->line_switch)
|
||||
i = 3;
|
||||
else
|
||||
i = 2;
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->value.enumerated.items = i;
|
||||
uinfo->count = 1;
|
||||
if (uinfo->value.enumerated.item >= i)
|
||||
uinfo->value.enumerated.item = i-1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
texts[uinfo->value.enumerated.item]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
unsigned int vref = stac92xx_vref_get(codec, nid);
|
||||
|
||||
if (vref == stac92xx_get_default_vref(codec, nid))
|
||||
ucontrol->value.enumerated.item[0] = 0;
|
||||
else if (vref == AC_PINCTL_VREF_GRD)
|
||||
ucontrol->value.enumerated.item[0] = 1;
|
||||
else if (vref == AC_PINCTL_VREF_HIZ)
|
||||
ucontrol->value.enumerated.item[0] = 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int new_vref = 0;
|
||||
unsigned int error;
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
|
||||
if (ucontrol->value.enumerated.item[0] == 0)
|
||||
new_vref = stac92xx_get_default_vref(codec, nid);
|
||||
else if (ucontrol->value.enumerated.item[0] == 1)
|
||||
new_vref = AC_PINCTL_VREF_GRD;
|
||||
else if (ucontrol->value.enumerated.item[0] == 2)
|
||||
new_vref = AC_PINCTL_VREF_HIZ;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (new_vref != stac92xx_vref_get(codec, nid)) {
|
||||
error = stac92xx_vref_set(codec, nid, new_vref);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[2];
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
if (kcontrol->private_value == spec->line_switch)
|
||||
texts[0] = "Line In";
|
||||
else
|
||||
texts[0] = "Mic In";
|
||||
texts[1] = "Line Out";
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->value.enumerated.items = 2;
|
||||
uinfo->count = 1;
|
||||
|
||||
if (uinfo->value.enumerated.item >= 2)
|
||||
uinfo->value.enumerated.item = 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
texts[uinfo->value.enumerated.item]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int io_idx = kcontrol-> private_value & 0xff;
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
int io_idx = (nid == spec->mic_switch) ? 1 : 0;
|
||||
|
||||
ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
|
||||
ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2615,9 +2762,9 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
hda_nid_t nid = kcontrol->private_value >> 8;
|
||||
int io_idx = kcontrol-> private_value & 0xff;
|
||||
unsigned short val = !!ucontrol->value.integer.value[0];
|
||||
hda_nid_t nid = kcontrol->private_value;
|
||||
int io_idx = (nid == spec->mic_switch) ? 1 : 0;
|
||||
unsigned short val = !!ucontrol->value.enumerated.item[0];
|
||||
|
||||
spec->io_switch[io_idx] = val;
|
||||
|
||||
|
@ -2626,7 +2773,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
else {
|
||||
unsigned int pinctl = AC_PINCTL_IN_EN;
|
||||
if (io_idx) /* set VREF for mic */
|
||||
pinctl |= stac92xx_get_vref(codec, nid);
|
||||
pinctl |= stac92xx_get_default_vref(codec, nid);
|
||||
stac92xx_auto_set_pinctl(codec, nid, pinctl);
|
||||
}
|
||||
|
||||
|
@ -2707,7 +2854,8 @@ enum {
|
|||
STAC_CTL_WIDGET_AMP_VOL,
|
||||
STAC_CTL_WIDGET_HP_SWITCH,
|
||||
STAC_CTL_WIDGET_IO_SWITCH,
|
||||
STAC_CTL_WIDGET_CLFE_SWITCH
|
||||
STAC_CTL_WIDGET_CLFE_SWITCH,
|
||||
STAC_CTL_WIDGET_DC_BIAS
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new stac92xx_control_templates[] = {
|
||||
|
@ -2719,6 +2867,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
|
|||
STAC_CODEC_HP_SWITCH(NULL),
|
||||
STAC_CODEC_IO_SWITCH(NULL, 0),
|
||||
STAC_CODEC_CLFE_SWITCH(NULL, 0),
|
||||
DC_BIAS(NULL, 0, 0),
|
||||
};
|
||||
|
||||
/* add dynamic controls */
|
||||
|
@ -2782,6 +2931,34 @@ static struct snd_kcontrol_new stac_input_src_temp = {
|
|||
.put = stac92xx_mux_enum_put,
|
||||
};
|
||||
|
||||
static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
|
||||
hda_nid_t nid, int idx)
|
||||
{
|
||||
int def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
||||
int control = 0;
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
char name[22];
|
||||
|
||||
if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
|
||||
if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
|
||||
&& nid == spec->line_switch)
|
||||
control = STAC_CTL_WIDGET_IO_SWITCH;
|
||||
else if (snd_hda_query_pin_caps(codec, nid)
|
||||
& (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
|
||||
control = STAC_CTL_WIDGET_DC_BIAS;
|
||||
else if (nid == spec->mic_switch)
|
||||
control = STAC_CTL_WIDGET_IO_SWITCH;
|
||||
}
|
||||
|
||||
if (control) {
|
||||
strcpy(name, auto_pin_cfg_labels[idx]);
|
||||
return stac92xx_add_control(codec->spec, control,
|
||||
strcat(name, " Jack Mode"), nid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_add_input_source(struct sigmatel_spec *spec)
|
||||
{
|
||||
struct snd_kcontrol_new *knew;
|
||||
|
@ -3144,7 +3321,9 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
const struct auto_pin_cfg *cfg)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
hda_nid_t nid;
|
||||
int err;
|
||||
int idx;
|
||||
|
||||
err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
|
||||
spec->multiout.dac_nids,
|
||||
|
@ -3161,20 +3340,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (spec->line_switch) {
|
||||
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
|
||||
"Line In as Output Switch",
|
||||
spec->line_switch << 8);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spec->mic_switch) {
|
||||
err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
|
||||
"Mic as Output Switch",
|
||||
(spec->mic_switch << 8) | 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
|
||||
nid = cfg->input_pins[idx];
|
||||
if (nid) {
|
||||
err = stac92xx_add_jack_mode_control(codec, nid, idx);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3639,6 +3811,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
|
|||
err = snd_hda_attach_beep_device(codec, nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* IDT/STAC codecs have linear beep tone parameter */
|
||||
codec->beep->linear_tone = 1;
|
||||
/* if no beep switch is available, make its own one */
|
||||
caps = query_amp_caps(codec, nid, HDA_OUTPUT);
|
||||
if (codec->beep &&
|
||||
|
@ -4082,7 +4256,7 @@ static int stac92xx_init(struct hda_codec *codec)
|
|||
unsigned int pinctl, conf;
|
||||
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
|
||||
/* for mic pins, force to initialize */
|
||||
pinctl = stac92xx_get_vref(codec, nid);
|
||||
pinctl = stac92xx_get_default_vref(codec, nid);
|
||||
pinctl |= AC_PINCTL_IN_EN;
|
||||
stac92xx_auto_set_pinctl(codec, nid, pinctl);
|
||||
} else {
|
||||
|
@ -4535,17 +4709,19 @@ static int stac92xx_resume(struct hda_codec *codec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* using power check for controlling mute led of HP HDX notebooks
|
||||
* using power check for controlling mute led of HP notebooks
|
||||
* check for mute state only on Speakers (nid = 0x10)
|
||||
*
|
||||
* For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
|
||||
* the LED is NOT working properly !
|
||||
*
|
||||
* Changed name to reflect that it now works for any designated
|
||||
* model, not just HP HDX.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
|
||||
static int stac92xx_hp_check_power_status(struct hda_codec *codec,
|
||||
hda_nid_t nid)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
@ -4553,9 +4729,9 @@ static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
|
|||
if (nid == 0x10) {
|
||||
if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
|
||||
HDA_AMP_MUTE)
|
||||
spec->gpio_data &= ~0x08; /* orange */
|
||||
spec->gpio_data &= ~spec->gpio_led; /* orange */
|
||||
else
|
||||
spec->gpio_data |= 0x08; /* white */
|
||||
spec->gpio_data |= spec->gpio_led; /* white */
|
||||
|
||||
stac_gpio_set(codec, spec->gpio_mask,
|
||||
spec->gpio_dir,
|
||||
|
@ -5201,6 +5377,15 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
|
||||
snd_hda_sequence_write_cache(codec, unmute_init);
|
||||
|
||||
/* Some HP machines seem to have unstable codec communications
|
||||
* especially with ATI fglrx driver. For recovering from the
|
||||
* CORB/RIRB stall, allow the BUS reset and keep always sync
|
||||
*/
|
||||
if (spec->board_config == STAC_HP_DV5) {
|
||||
codec->bus->sync_write = 1;
|
||||
codec->bus->allow_bus_reset = 1;
|
||||
}
|
||||
|
||||
spec->aloopback_ctl = stac92hd71bxx_loopback;
|
||||
spec->aloopback_mask = 0x50;
|
||||
spec->aloopback_shift = 0;
|
||||
|
@ -5234,6 +5419,15 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
spec->num_smuxes = 0;
|
||||
spec->num_dmuxes = 1;
|
||||
break;
|
||||
case STAC_HP_DV4_1222NR:
|
||||
spec->num_dmics = 1;
|
||||
/* I don't know if it needs 1 or 2 smuxes - will wait for
|
||||
* bug reports to fix if needed
|
||||
*/
|
||||
spec->num_smuxes = 1;
|
||||
spec->num_dmuxes = 1;
|
||||
spec->gpio_led = 0x01;
|
||||
/* fallthrough */
|
||||
case STAC_HP_DV5:
|
||||
snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
|
||||
stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
|
||||
|
@ -5242,22 +5436,21 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
spec->num_dmics = 1;
|
||||
spec->num_dmuxes = 1;
|
||||
spec->num_smuxes = 1;
|
||||
/*
|
||||
* For controlling MUTE LED on HP HDX16/HDX18 notebooks,
|
||||
* the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
|
||||
*/
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* orange/white mute led on GPIO3, orange=0, white=1 */
|
||||
spec->gpio_mask |= 0x08;
|
||||
spec->gpio_dir |= 0x08;
|
||||
spec->gpio_data |= 0x08; /* set to white */
|
||||
spec->gpio_led = 0x08;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
if (spec->gpio_led) {
|
||||
spec->gpio_mask |= spec->gpio_led;
|
||||
spec->gpio_dir |= spec->gpio_led;
|
||||
spec->gpio_data |= spec->gpio_led;
|
||||
/* register check_power_status callback. */
|
||||
codec->patch_ops.check_power_status =
|
||||
stac92xx_hp_hdx_check_power_status;
|
||||
stac92xx_hp_check_power_status;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
};
|
||||
|
||||
spec->multiout.dac_nids = spec->dac_nids;
|
||||
if (spec->dinput_mux)
|
||||
|
@ -5282,7 +5475,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
|
|||
codec->proc_widget_hook = stac92hd7x_proc_hook;
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static int patch_stac922x(struct hda_codec *codec)
|
||||
{
|
||||
|
@ -5437,7 +5630,7 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
/* correct the device field to SPDIF out */
|
||||
snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
|
||||
break;
|
||||
};
|
||||
}
|
||||
/* configure the analog microphone on some laptops */
|
||||
snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
|
||||
/* correct the front output jack as a hp out */
|
||||
|
@ -5747,6 +5940,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
|
|||
{ .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
|
||||
{ .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
|
||||
{ .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
|
||||
|
|
|
@ -205,7 +205,7 @@ struct via_spec {
|
|||
|
||||
/* playback */
|
||||
struct hda_multi_out multiout;
|
||||
hda_nid_t extra_dig_out_nid;
|
||||
hda_nid_t slave_dig_outs[2];
|
||||
|
||||
/* capture */
|
||||
unsigned int num_adc_nids;
|
||||
|
@ -731,21 +731,6 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
|||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
/* setup SPDIF output stream */
|
||||
static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int stream_tag, unsigned int format)
|
||||
{
|
||||
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
|
||||
if (codec->spdif_ctls & AC_DIG1_ENABLE)
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
|
||||
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
|
||||
/* turn on again (if needed) */
|
||||
if (codec->spdif_ctls & AC_DIG1_ENABLE)
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
|
||||
codec->spdif_ctls & 0xff);
|
||||
}
|
||||
|
||||
static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
unsigned int stream_tag,
|
||||
|
@ -753,19 +738,16 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
hda_nid_t nid;
|
||||
return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
|
||||
stream_tag, format, substream);
|
||||
}
|
||||
|
||||
/* 1st or 2nd S/PDIF */
|
||||
if (substream->number == 0)
|
||||
nid = spec->multiout.dig_out_nid;
|
||||
else if (substream->number == 1)
|
||||
nid = spec->extra_dig_out_nid;
|
||||
else
|
||||
return -1;
|
||||
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
setup_dig_playback_stream(codec, nid, stream_tag, format);
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -842,7 +824,8 @@ static struct hda_pcm_stream vt1708_pcm_digital_playback = {
|
|||
.ops = {
|
||||
.open = via_dig_playback_pcm_open,
|
||||
.close = via_dig_playback_pcm_close,
|
||||
.prepare = via_dig_playback_pcm_prepare
|
||||
.prepare = via_dig_playback_pcm_prepare,
|
||||
.cleanup = via_dig_playback_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -874,13 +857,6 @@ static int via_build_controls(struct hda_codec *codec)
|
|||
if (err < 0)
|
||||
return err;
|
||||
spec->multiout.share_spdif = 1;
|
||||
|
||||
if (spec->extra_dig_out_nid) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->extra_dig_out_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (spec->dig_in_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
||||
|
@ -1013,10 +989,6 @@ static void via_unsol_event(struct hda_codec *codec,
|
|||
via_gpio_control(codec);
|
||||
}
|
||||
|
||||
static hda_nid_t slave_dig_outs[] = {
|
||||
0,
|
||||
};
|
||||
|
||||
static int via_init(struct hda_codec *codec)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
|
@ -1051,8 +1023,9 @@ static int via_init(struct hda_codec *codec)
|
|||
snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
|
||||
|
||||
/* no slave outs */
|
||||
codec->slave_dig_outs = slave_dig_outs;
|
||||
/* assign slave outs */
|
||||
if (spec->slave_dig_outs[0])
|
||||
codec->slave_dig_outs = spec->slave_dig_outs;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2134,7 +2107,8 @@ static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
|
|||
.ops = {
|
||||
.open = via_dig_playback_pcm_open,
|
||||
.close = via_dig_playback_pcm_close,
|
||||
.prepare = via_dig_playback_pcm_prepare
|
||||
.prepare = via_dig_playback_pcm_prepare,
|
||||
.cleanup = via_dig_playback_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2589,14 +2563,15 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
|
|||
};
|
||||
|
||||
static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
|
||||
.substreams = 2,
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
/* NID is set in via_build_pcms */
|
||||
.ops = {
|
||||
.open = via_dig_playback_pcm_open,
|
||||
.close = via_dig_playback_pcm_close,
|
||||
.prepare = via_dig_playback_pcm_prepare
|
||||
.prepare = via_dig_playback_pcm_prepare,
|
||||
.cleanup = via_dig_playback_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2805,14 +2780,37 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* fill out digital output widgets; one for master and one for slave outputs */
|
||||
static void fill_dig_outs(struct hda_codec *codec)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < spec->autocfg.dig_outs; i++) {
|
||||
hda_nid_t nid;
|
||||
int conn;
|
||||
|
||||
nid = spec->autocfg.dig_out_pins[i];
|
||||
if (!nid)
|
||||
continue;
|
||||
conn = snd_hda_get_connections(codec, nid, &nid, 1);
|
||||
if (conn < 1)
|
||||
continue;
|
||||
if (!spec->multiout.dig_out_nid)
|
||||
spec->multiout.dig_out_nid = nid;
|
||||
else {
|
||||
spec->slave_dig_outs[0] = nid;
|
||||
break; /* at most two dig outs */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vt1708S_parse_auto_config(struct hda_codec *codec)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
int err;
|
||||
static hda_nid_t vt1708s_ignore[] = {0x21, 0};
|
||||
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
|
||||
vt1708s_ignore);
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
|
||||
|
@ -2833,10 +2831,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
|
|||
|
||||
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
|
||||
|
||||
if (spec->autocfg.dig_outs)
|
||||
spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
|
||||
|
||||
spec->extra_dig_out_nid = 0x15;
|
||||
fill_dig_outs(codec);
|
||||
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
@ -3000,7 +2995,8 @@ static struct hda_pcm_stream vt1702_pcm_digital_playback = {
|
|||
.ops = {
|
||||
.open = via_dig_playback_pcm_open,
|
||||
.close = via_dig_playback_pcm_close,
|
||||
.prepare = via_dig_playback_pcm_prepare
|
||||
.prepare = via_dig_playback_pcm_prepare,
|
||||
.cleanup = via_dig_playback_pcm_cleanup
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3128,10 +3124,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
|
|||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
int err;
|
||||
static hda_nid_t vt1702_ignore[] = {0x1C, 0};
|
||||
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
|
||||
vt1702_ignore);
|
||||
err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
|
||||
|
@ -3152,10 +3146,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
|
|||
|
||||
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
|
||||
|
||||
if (spec->autocfg.dig_outs)
|
||||
spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
|
||||
|
||||
spec->extra_dig_out_nid = 0x1B;
|
||||
fill_dig_outs(codec);
|
||||
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
snd-ice17xx-ak4xxx-objs := ak4xxx.o
|
||||
snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
|
||||
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
|
||||
snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
|
||||
|
|
|
@ -335,6 +335,7 @@ struct snd_ice1712 {
|
|||
unsigned int force_rdma1:1; /* VT1720/4 - RDMA1 as non-spdif */
|
||||
unsigned int midi_output:1; /* VT1720/4: MIDI output triggered */
|
||||
unsigned int midi_input:1; /* VT1720/4: MIDI input triggered */
|
||||
unsigned int own_routing:1; /* VT1720/4: use own routing ctls */
|
||||
unsigned int num_total_dacs; /* total DACs */
|
||||
unsigned int num_total_adcs; /* total ADCs */
|
||||
unsigned int cur_rate; /* current rate */
|
||||
|
@ -458,10 +459,17 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
|
|||
return snd_ice1712_gpio_read(ice) & mask;
|
||||
}
|
||||
|
||||
/* route access functions */
|
||||
int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift);
|
||||
int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
|
||||
int shift);
|
||||
|
||||
int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
|
||||
|
||||
int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template,
|
||||
const struct snd_ak4xxx_private *priv, struct snd_ice1712 *ice);
|
||||
int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak,
|
||||
const struct snd_akm4xxx *template,
|
||||
const struct snd_ak4xxx_private *priv,
|
||||
struct snd_ice1712 *ice);
|
||||
void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice);
|
||||
int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "prodigy192.h"
|
||||
#include "prodigy_hifi.h"
|
||||
#include "juli.h"
|
||||
#include "maya44.h"
|
||||
#include "phase.h"
|
||||
#include "wtm.h"
|
||||
#include "se.h"
|
||||
|
@ -65,6 +66,7 @@ MODULE_SUPPORTED_DEVICE("{"
|
|||
PRODIGY192_DEVICE_DESC
|
||||
PRODIGY_HIFI_DEVICE_DESC
|
||||
JULI_DEVICE_DESC
|
||||
MAYA44_DEVICE_DESC
|
||||
PHASE_DEVICE_DESC
|
||||
WTM_DEVICE_DESC
|
||||
SE_DEVICE_DESC
|
||||
|
@ -626,7 +628,7 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
||||
static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
||||
int force)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -634,17 +636,18 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
unsigned int i, old_rate;
|
||||
|
||||
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&ice->reg_lock, flags);
|
||||
if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
|
||||
(inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
|
||||
/* running? we cannot change the rate now... */
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return;
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!force && is_pro_rate_locked(ice)) {
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return;
|
||||
return (rate == ice->cur_rate) ? 0 : -EBUSY;
|
||||
}
|
||||
|
||||
old_rate = ice->get_rate(ice);
|
||||
|
@ -652,7 +655,7 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
ice->set_rate(ice, rate);
|
||||
else if (rate == ice->cur_rate) {
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ice->cur_rate = rate;
|
||||
|
@ -674,13 +677,15 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
}
|
||||
if (ice->spdif.ops.setup_rate)
|
||||
ice->spdif.ops.setup_rate(ice, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
int i, chs;
|
||||
int i, chs, err;
|
||||
|
||||
chs = params_channels(hw_params);
|
||||
mutex_lock(&ice->open_mutex);
|
||||
|
@ -715,7 +720,11 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
}
|
||||
mutex_unlock(&ice->open_mutex);
|
||||
snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
|
||||
|
||||
err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
||||
}
|
||||
|
||||
|
@ -848,20 +857,39 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
|
|||
#endif
|
||||
}
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
|
||||
static const struct vt1724_pcm_reg vt1724_pdma0_reg = {
|
||||
.addr = VT1724_MT_PLAYBACK_ADDR,
|
||||
.size = VT1724_MT_PLAYBACK_SIZE,
|
||||
.count = VT1724_MT_PLAYBACK_COUNT,
|
||||
.start = VT1724_PDMA0_START,
|
||||
};
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
|
||||
static const struct vt1724_pcm_reg vt1724_pdma4_reg = {
|
||||
.addr = VT1724_MT_PDMA4_ADDR,
|
||||
.size = VT1724_MT_PDMA4_SIZE,
|
||||
.count = VT1724_MT_PDMA4_COUNT,
|
||||
.start = VT1724_PDMA4_START,
|
||||
};
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_rdma0_reg = {
|
||||
.addr = VT1724_MT_CAPTURE_ADDR,
|
||||
.size = VT1724_MT_CAPTURE_SIZE,
|
||||
.count = VT1724_MT_CAPTURE_COUNT,
|
||||
.start = VT1724_RDMA0_START,
|
||||
};
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_rdma1_reg = {
|
||||
.addr = VT1724_MT_RDMA1_ADDR,
|
||||
.size = VT1724_MT_RDMA1_SIZE,
|
||||
.count = VT1724_MT_RDMA1_COUNT,
|
||||
.start = VT1724_RDMA1_START,
|
||||
};
|
||||
|
||||
#define vt1724_playback_pro_reg vt1724_pdma0_reg
|
||||
#define vt1724_playback_spdif_reg vt1724_pdma4_reg
|
||||
#define vt1724_capture_pro_reg vt1724_rdma0_reg
|
||||
#define vt1724_capture_spdif_reg vt1724_rdma1_reg
|
||||
|
||||
static const struct snd_pcm_hardware snd_vt1724_playback_pro = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
|
@ -1077,20 +1105,6 @@ static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
|
|||
* SPDIF PCM
|
||||
*/
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
|
||||
.addr = VT1724_MT_PDMA4_ADDR,
|
||||
.size = VT1724_MT_PDMA4_SIZE,
|
||||
.count = VT1724_MT_PDMA4_COUNT,
|
||||
.start = VT1724_PDMA4_START,
|
||||
};
|
||||
|
||||
static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
|
||||
.addr = VT1724_MT_RDMA1_ADDR,
|
||||
.size = VT1724_MT_RDMA1_SIZE,
|
||||
.count = VT1724_MT_RDMA1_COUNT,
|
||||
.start = VT1724_RDMA1_START,
|
||||
};
|
||||
|
||||
/* update spdif control bits; call with reg_lock */
|
||||
static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
|
||||
{
|
||||
|
@ -1963,7 +1977,7 @@ static inline int digital_route_shift(int idx)
|
|||
return idx * 3;
|
||||
}
|
||||
|
||||
static int get_route_val(struct snd_ice1712 *ice, int shift)
|
||||
int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift)
|
||||
{
|
||||
unsigned long val;
|
||||
unsigned char eitem;
|
||||
|
@ -1982,7 +1996,8 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)
|
|||
return eitem;
|
||||
}
|
||||
|
||||
static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
|
||||
int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
|
||||
int shift)
|
||||
{
|
||||
unsigned int old_val, nval;
|
||||
int change;
|
||||
|
@ -2010,7 +2025,7 @@ static int snd_vt1724_pro_route_analog_get(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
get_route_val(ice, analog_route_shift(idx));
|
||||
snd_ice1724_get_route_val(ice, analog_route_shift(idx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2019,8 +2034,9 @@ static int snd_vt1724_pro_route_analog_put(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
return put_route_val(ice, ucontrol->value.enumerated.item[0],
|
||||
analog_route_shift(idx));
|
||||
return snd_ice1724_put_route_val(ice,
|
||||
ucontrol->value.enumerated.item[0],
|
||||
analog_route_shift(idx));
|
||||
}
|
||||
|
||||
static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
|
||||
|
@ -2029,7 +2045,7 @@ static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
get_route_val(ice, digital_route_shift(idx));
|
||||
snd_ice1724_get_route_val(ice, digital_route_shift(idx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2038,11 +2054,13 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
|
|||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
return put_route_val(ice, ucontrol->value.enumerated.item[0],
|
||||
digital_route_shift(idx));
|
||||
return snd_ice1724_put_route_val(ice,
|
||||
ucontrol->value.enumerated.item[0],
|
||||
digital_route_shift(idx));
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
|
||||
static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "H/W Playback Route",
|
||||
.info = snd_vt1724_pro_route_info,
|
||||
|
@ -2109,6 +2127,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
|
|||
snd_vt1724_prodigy_hifi_cards,
|
||||
snd_vt1724_prodigy192_cards,
|
||||
snd_vt1724_juli_cards,
|
||||
snd_vt1724_maya44_cards,
|
||||
snd_vt1724_phase_cards,
|
||||
snd_vt1724_wtm_cards,
|
||||
snd_vt1724_se_cards,
|
||||
|
@ -2246,8 +2265,10 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
|
|||
static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
||||
{
|
||||
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
|
||||
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
|
||||
msleep(10);
|
||||
outb(0, ICEREG1724(ice, CONTROL));
|
||||
inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
|
@ -2277,9 +2298,12 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
|
|||
if (snd_BUG_ON(!ice->pcm))
|
||||
return -EIO;
|
||||
|
||||
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!ice->own_routing) {
|
||||
err = snd_ctl_add(ice->card,
|
||||
snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
|
||||
if (err < 0)
|
||||
|
@ -2326,7 +2350,7 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ice->num_total_dacs > 0) {
|
||||
if (!ice->own_routing && ice->num_total_dacs > 0) {
|
||||
struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route;
|
||||
tmp.count = ice->num_total_dacs;
|
||||
if (ice->vt1720 && tmp.count > 2)
|
||||
|
|
779
sound/pci/ice1712/maya44.c
Normal file
779
sound/pci/ice1712/maya44.c
Normal file
|
@ -0,0 +1,779 @@
|
|||
/*
|
||||
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
|
||||
*
|
||||
* Lowlevel functions for ESI Maya44 cards
|
||||
*
|
||||
* Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
|
||||
* Based on the patches by Rainer Zimmermann <mail@lightshed.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "ice1712.h"
|
||||
#include "envy24ht.h"
|
||||
#include "maya44.h"
|
||||
|
||||
/* WM8776 register indexes */
|
||||
#define WM8776_REG_HEADPHONE_L 0x00
|
||||
#define WM8776_REG_HEADPHONE_R 0x01
|
||||
#define WM8776_REG_HEADPHONE_MASTER 0x02
|
||||
#define WM8776_REG_DAC_ATTEN_L 0x03
|
||||
#define WM8776_REG_DAC_ATTEN_R 0x04
|
||||
#define WM8776_REG_DAC_ATTEN_MASTER 0x05
|
||||
#define WM8776_REG_DAC_PHASE 0x06
|
||||
#define WM8776_REG_DAC_CONTROL 0x07
|
||||
#define WM8776_REG_DAC_MUTE 0x08
|
||||
#define WM8776_REG_DAC_DEEMPH 0x09
|
||||
#define WM8776_REG_DAC_IF_CONTROL 0x0a
|
||||
#define WM8776_REG_ADC_IF_CONTROL 0x0b
|
||||
#define WM8776_REG_MASTER_MODE_CONTROL 0x0c
|
||||
#define WM8776_REG_POWERDOWN 0x0d
|
||||
#define WM8776_REG_ADC_ATTEN_L 0x0e
|
||||
#define WM8776_REG_ADC_ATTEN_R 0x0f
|
||||
#define WM8776_REG_ADC_ALC1 0x10
|
||||
#define WM8776_REG_ADC_ALC2 0x11
|
||||
#define WM8776_REG_ADC_ALC3 0x12
|
||||
#define WM8776_REG_ADC_NOISE_GATE 0x13
|
||||
#define WM8776_REG_ADC_LIMITER 0x14
|
||||
#define WM8776_REG_ADC_MUX 0x15
|
||||
#define WM8776_REG_OUTPUT_MUX 0x16
|
||||
#define WM8776_REG_RESET 0x17
|
||||
|
||||
#define WM8776_NUM_REGS 0x18
|
||||
|
||||
/* clock ratio identifiers for snd_wm8776_set_rate() */
|
||||
#define WM8776_CLOCK_RATIO_128FS 0
|
||||
#define WM8776_CLOCK_RATIO_192FS 1
|
||||
#define WM8776_CLOCK_RATIO_256FS 2
|
||||
#define WM8776_CLOCK_RATIO_384FS 3
|
||||
#define WM8776_CLOCK_RATIO_512FS 4
|
||||
#define WM8776_CLOCK_RATIO_768FS 5
|
||||
|
||||
enum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS };
|
||||
enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES };
|
||||
|
||||
struct snd_wm8776 {
|
||||
unsigned char addr;
|
||||
unsigned short regs[WM8776_NUM_REGS];
|
||||
unsigned char volumes[WM_NUM_VOLS][2];
|
||||
unsigned int switch_bits;
|
||||
};
|
||||
|
||||
struct snd_maya44 {
|
||||
struct snd_ice1712 *ice;
|
||||
struct snd_wm8776 wm[2];
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
|
||||
/* write the given register and save the data to the cache */
|
||||
static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
|
||||
unsigned char reg, unsigned short val)
|
||||
{
|
||||
/*
|
||||
* WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
|
||||
* of the address field
|
||||
*/
|
||||
snd_vt1724_write_i2c(ice, wm->addr,
|
||||
(reg << 1) | ((val >> 8) & 1),
|
||||
val & 0xff);
|
||||
wm->regs[reg] = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* update the given register with and/or mask and save the data to the cache
|
||||
*/
|
||||
static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
|
||||
unsigned char reg,
|
||||
unsigned short mask, unsigned short val)
|
||||
{
|
||||
val |= wm->regs[reg] & ~mask;
|
||||
if (val != wm->regs[reg]) {
|
||||
wm8776_write(ice, wm, reg, val);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* WM8776 volume controls
|
||||
*/
|
||||
|
||||
struct maya_vol_info {
|
||||
unsigned int maxval; /* volume range: 0..maxval */
|
||||
unsigned char regs[2]; /* left and right registers */
|
||||
unsigned short mask; /* value mask */
|
||||
unsigned short offset; /* zero-value offset */
|
||||
unsigned short mute; /* mute bit */
|
||||
unsigned short update; /* update bits */
|
||||
unsigned char mux_bits[2]; /* extra bits for ADC mute */
|
||||
};
|
||||
|
||||
static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
|
||||
[WM_VOL_HP] = {
|
||||
.maxval = 80,
|
||||
.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
|
||||
.mask = 0x7f,
|
||||
.offset = 0x30,
|
||||
.mute = 0x00,
|
||||
.update = 0x180, /* update and zero-cross enable */
|
||||
},
|
||||
[WM_VOL_DAC] = {
|
||||
.maxval = 255,
|
||||
.regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R },
|
||||
.mask = 0xff,
|
||||
.offset = 0x01,
|
||||
.mute = 0x00,
|
||||
.update = 0x100, /* zero-cross enable */
|
||||
},
|
||||
[WM_VOL_ADC] = {
|
||||
.maxval = 91,
|
||||
.regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R },
|
||||
.mask = 0xff,
|
||||
.offset = 0xa5,
|
||||
.mute = 0xa5,
|
||||
.update = 0x100, /* update */
|
||||
.mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* dB tables
|
||||
*/
|
||||
/* headphone output: mute, -73..+6db (1db step) */
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1);
|
||||
/* DAC output: mute, -127..0db (0.5db step) */
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1);
|
||||
/* ADC gain: mute, -21..+24db (0.5db step) */
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1);
|
||||
|
||||
static int maya_vol_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
unsigned int idx = kcontrol->private_value;
|
||||
struct maya_vol_info *vol = &vol_info[idx];
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->count = 2;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = vol->maxval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_vol_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_wm8776 *wm =
|
||||
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
|
||||
unsigned int idx = kcontrol->private_value;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
ucontrol->value.integer.value[0] = wm->volumes[idx][0];
|
||||
ucontrol->value.integer.value[1] = wm->volumes[idx][1];
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_vol_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_wm8776 *wm =
|
||||
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
|
||||
unsigned int idx = kcontrol->private_value;
|
||||
struct maya_vol_info *vol = &vol_info[idx];
|
||||
unsigned int val, data;
|
||||
int ch, changed = 0;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
val = ucontrol->value.integer.value[ch];
|
||||
if (val > vol->maxval)
|
||||
val = vol->maxval;
|
||||
if (val == wm->volumes[idx][ch])
|
||||
continue;
|
||||
if (!val)
|
||||
data = vol->mute;
|
||||
else
|
||||
data = (val - 1) + vol->offset;
|
||||
data |= vol->update;
|
||||
changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch],
|
||||
vol->mask | vol->update, data);
|
||||
if (vol->mux_bits[ch])
|
||||
wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX,
|
||||
vol->mux_bits[ch],
|
||||
val ? 0 : vol->mux_bits[ch]);
|
||||
wm->volumes[idx][ch] = val;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* WM8776 switch controls
|
||||
*/
|
||||
|
||||
#define COMPOSE_SW_VAL(idx, reg, mask) ((idx) | ((reg) << 8) | ((mask) << 16))
|
||||
#define GET_SW_VAL_IDX(val) ((val) & 0xff)
|
||||
#define GET_SW_VAL_REG(val) (((val) >> 8) & 0xff)
|
||||
#define GET_SW_VAL_MASK(val) (((val) >> 16) & 0xff)
|
||||
|
||||
#define maya_sw_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int maya_sw_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_wm8776 *wm =
|
||||
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
|
||||
unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
|
||||
|
||||
ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
struct snd_wm8776 *wm =
|
||||
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
|
||||
unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
|
||||
unsigned int mask, val;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
mask = 1 << idx;
|
||||
wm->switch_bits &= ~mask;
|
||||
val = ucontrol->value.integer.value[0];
|
||||
if (val)
|
||||
wm->switch_bits |= mask;
|
||||
mask = GET_SW_VAL_MASK(kcontrol->private_value);
|
||||
changed = wm8776_write_bits(chip->ice, wm,
|
||||
GET_SW_VAL_REG(kcontrol->private_value),
|
||||
mask, val ? mask : 0);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPIO pins (known ones for maya44)
|
||||
*/
|
||||
#define GPIO_PHANTOM_OFF 2
|
||||
#define GPIO_MIC_RELAY 4
|
||||
#define GPIO_SPDIF_IN_INV 5
|
||||
#define GPIO_MUST_BE_0 7
|
||||
|
||||
/*
|
||||
* GPIO switch controls
|
||||
*/
|
||||
|
||||
#define COMPOSE_GPIO_VAL(shift, inv) ((shift) | ((inv) << 8))
|
||||
#define GET_GPIO_VAL_SHIFT(val) ((val) & 0xff)
|
||||
#define GET_GPIO_VAL_INV(val) (((val) >> 8) & 1)
|
||||
|
||||
static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
|
||||
unsigned int bits)
|
||||
{
|
||||
unsigned int data;
|
||||
data = snd_ice1712_gpio_read(ice);
|
||||
if ((data & mask) == bits)
|
||||
return 0;
|
||||
snd_ice1712_gpio_write(ice, (data & ~mask) | bits);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define maya_gpio_sw_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
|
||||
unsigned int val;
|
||||
|
||||
val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1;
|
||||
if (GET_GPIO_VAL_INV(kcontrol->private_value))
|
||||
val = !val;
|
||||
ucontrol->value.integer.value[0] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
|
||||
unsigned int val, mask;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
mask = 1 << shift;
|
||||
val = ucontrol->value.integer.value[0];
|
||||
if (GET_GPIO_VAL_INV(kcontrol->private_value))
|
||||
val = !val;
|
||||
val = val ? mask : 0;
|
||||
changed = maya_set_gpio_bits(chip->ice, mask, val);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* capture source selection
|
||||
*/
|
||||
|
||||
/* known working input slots (0-4) */
|
||||
#define MAYA_LINE_IN 1 /* in-2 */
|
||||
#define MAYA_MIC_IN 4 /* in-5 */
|
||||
|
||||
static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
|
||||
{
|
||||
wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX,
|
||||
0x1f, 1 << line);
|
||||
}
|
||||
|
||||
static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[] = { "Line", "Mic" };
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = ARRAY_SIZE(texts);
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item =
|
||||
uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int sel;
|
||||
|
||||
if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY))
|
||||
sel = 1;
|
||||
else
|
||||
sel = 0;
|
||||
ucontrol->value.enumerated.item[0] = sel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int sel = ucontrol->value.enumerated.item[0];
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY,
|
||||
sel ? GPIO_MIC_RELAY : 0);
|
||||
wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Maya44 routing switch settings have different meanings than the standard
|
||||
* ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
|
||||
*/
|
||||
static int maya_pb_route_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[] = {
|
||||
"PCM Out", /* 0 */
|
||||
"Input 1", "Input 2", "Input 3", "Input 4"
|
||||
};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = ARRAY_SIZE(texts);
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item =
|
||||
uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_pb_route_shift(int idx)
|
||||
{
|
||||
static const unsigned char shift[10] =
|
||||
{ 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 };
|
||||
return shift[idx % 10];
|
||||
}
|
||||
|
||||
static int maya_pb_route_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
ucontrol->value.enumerated.item[0] =
|
||||
snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
return snd_ice1724_put_route_val(chip->ice,
|
||||
ucontrol->value.enumerated.item[0],
|
||||
maya_pb_route_shift(idx));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* controls to be added
|
||||
*/
|
||||
|
||||
static struct snd_kcontrol_new maya_controls[] __devinitdata = {
|
||||
{
|
||||
.name = "Crossmix Playback Volume",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = maya_vol_info,
|
||||
.get = maya_vol_get,
|
||||
.put = maya_vol_put,
|
||||
.tlv = { .p = db_scale_hp },
|
||||
.private_value = WM_VOL_HP,
|
||||
.count = 2,
|
||||
},
|
||||
{
|
||||
.name = "PCM Playback Volume",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = maya_vol_info,
|
||||
.get = maya_vol_get,
|
||||
.put = maya_vol_put,
|
||||
.tlv = { .p = db_scale_dac },
|
||||
.private_value = WM_VOL_DAC,
|
||||
.count = 2,
|
||||
},
|
||||
{
|
||||
.name = "Line Capture Volume",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = maya_vol_info,
|
||||
.get = maya_vol_get,
|
||||
.put = maya_vol_put,
|
||||
.tlv = { .p = db_scale_adc },
|
||||
.private_value = WM_VOL_ADC,
|
||||
.count = 2,
|
||||
},
|
||||
{
|
||||
.name = "PCM Playback Switch",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = maya_sw_info,
|
||||
.get = maya_sw_get,
|
||||
.put = maya_sw_put,
|
||||
.private_value = COMPOSE_SW_VAL(WM_SW_DAC,
|
||||
WM8776_REG_OUTPUT_MUX, 0x01),
|
||||
.count = 2,
|
||||
},
|
||||
{
|
||||
.name = "Bypass Playback Switch",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = maya_sw_info,
|
||||
.get = maya_sw_get,
|
||||
.put = maya_sw_put,
|
||||
.private_value = COMPOSE_SW_VAL(WM_SW_BYPASS,
|
||||
WM8776_REG_OUTPUT_MUX, 0x04),
|
||||
.count = 2,
|
||||
},
|
||||
{
|
||||
.name = "Capture Source",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = maya_rec_src_info,
|
||||
.get = maya_rec_src_get,
|
||||
.put = maya_rec_src_put,
|
||||
},
|
||||
{
|
||||
.name = "Mic Phantom Power Switch",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = maya_gpio_sw_info,
|
||||
.get = maya_gpio_sw_get,
|
||||
.put = maya_gpio_sw_put,
|
||||
.private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1),
|
||||
},
|
||||
{
|
||||
.name = "SPDIF Capture Switch",
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = maya_gpio_sw_info,
|
||||
.get = maya_gpio_sw_get,
|
||||
.put = maya_gpio_sw_put,
|
||||
.private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1),
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "H/W Playback Route",
|
||||
.info = maya_pb_route_info,
|
||||
.get = maya_pb_route_get,
|
||||
.put = maya_pb_route_put,
|
||||
.count = 4, /* FIXME: do controls 5-9 have any meaning? */
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(maya_controls); i++) {
|
||||
err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i],
|
||||
ice->spec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialize a wm8776 chip
|
||||
*/
|
||||
static void __devinit wm8776_init(struct snd_ice1712 *ice,
|
||||
struct snd_wm8776 *wm, unsigned int addr)
|
||||
{
|
||||
static const unsigned short inits_wm8776[] = {
|
||||
0x02, 0x100, /* R2: headphone L+R muted + update */
|
||||
0x05, 0x100, /* R5: DAC output L+R muted + update */
|
||||
0x06, 0x000, /* R6: DAC output phase normal */
|
||||
0x07, 0x091, /* R7: DAC enable zero cross detection,
|
||||
normal output */
|
||||
0x08, 0x000, /* R8: DAC soft mute off */
|
||||
0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */
|
||||
0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */
|
||||
0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit,
|
||||
highpass filter enabled */
|
||||
0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */
|
||||
0x0d, 0x000, /* R13: all power up */
|
||||
0x0e, 0x100, /* R14: ADC left muted,
|
||||
enable zero cross detection */
|
||||
0x0f, 0x100, /* R15: ADC right muted,
|
||||
enable zero cross detection */
|
||||
/* R16: ALC...*/
|
||||
0x11, 0x000, /* R17: disable ALC */
|
||||
/* R18: ALC...*/
|
||||
/* R19: noise gate...*/
|
||||
0x15, 0x000, /* R21: ADC input mux init, mute all inputs */
|
||||
0x16, 0x001, /* R22: output mux, select DAC */
|
||||
0xff, 0xff
|
||||
};
|
||||
|
||||
const unsigned short *ptr;
|
||||
unsigned char reg;
|
||||
unsigned short data;
|
||||
|
||||
wm->addr = addr;
|
||||
/* enable DAC output; mute bypass, aux & all inputs */
|
||||
wm->switch_bits = (1 << WM_SW_DAC);
|
||||
|
||||
ptr = inits_wm8776;
|
||||
while (*ptr != 0xff) {
|
||||
reg = *ptr++;
|
||||
data = *ptr++;
|
||||
wm8776_write(ice, wm, reg, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* change the rate on the WM8776 codecs.
|
||||
* this assumes that the VT17xx's rate is changed by the calling function.
|
||||
* NOTE: even though the WM8776's are running in slave mode and rate
|
||||
* selection is automatic, we need to call snd_wm8776_set_rate() here
|
||||
* to make sure some flags are set correctly.
|
||||
*/
|
||||
static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
|
||||
{
|
||||
struct snd_maya44 *chip = ice->spec;
|
||||
unsigned int ratio, adc_ratio, val;
|
||||
int i;
|
||||
|
||||
switch (rate) {
|
||||
case 192000:
|
||||
ratio = WM8776_CLOCK_RATIO_128FS;
|
||||
break;
|
||||
case 176400:
|
||||
ratio = WM8776_CLOCK_RATIO_128FS;
|
||||
break;
|
||||
case 96000:
|
||||
ratio = WM8776_CLOCK_RATIO_256FS;
|
||||
break;
|
||||
case 88200:
|
||||
ratio = WM8776_CLOCK_RATIO_384FS;
|
||||
break;
|
||||
case 48000:
|
||||
ratio = WM8776_CLOCK_RATIO_512FS;
|
||||
break;
|
||||
case 44100:
|
||||
ratio = WM8776_CLOCK_RATIO_512FS;
|
||||
break;
|
||||
case 32000:
|
||||
ratio = WM8776_CLOCK_RATIO_768FS;
|
||||
break;
|
||||
case 0:
|
||||
/* no hint - S/PDIF input is master, simply return */
|
||||
return;
|
||||
default:
|
||||
snd_BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* this currently sets the same rate for ADC and DAC, but limits
|
||||
* ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC
|
||||
* oversampling to 64x, as recommended by WM8776 datasheet.
|
||||
* Setting the rate is not really necessary in slave mode.
|
||||
*/
|
||||
adc_ratio = ratio;
|
||||
if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
|
||||
adc_ratio = WM8776_CLOCK_RATIO_256FS;
|
||||
|
||||
val = adc_ratio;
|
||||
if (adc_ratio == WM8776_CLOCK_RATIO_256FS)
|
||||
val |= 8;
|
||||
val |= ratio << 4;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
for (i = 0; i < 2; i++)
|
||||
wm8776_write_bits(ice, &chip->wm[i],
|
||||
WM8776_REG_MASTER_MODE_CONTROL,
|
||||
0x180, val);
|
||||
mutex_unlock(&chip->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* supported sample rates (to override the default one)
|
||||
*/
|
||||
|
||||
static unsigned int rates[] = {
|
||||
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
|
||||
};
|
||||
|
||||
/* playback rates: 32..192 kHz */
|
||||
static struct snd_pcm_hw_constraint_list dac_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* chip addresses on I2C bus
|
||||
*/
|
||||
static unsigned char wm8776_addr[2] __devinitdata = {
|
||||
0x34, 0x36, /* codec 0 & 1 */
|
||||
};
|
||||
|
||||
/*
|
||||
* initialize the chip
|
||||
*/
|
||||
static int __devinit maya44_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
int i;
|
||||
struct snd_maya44 *chip;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
mutex_init(&chip->mutex);
|
||||
chip->ice = ice;
|
||||
ice->spec = chip;
|
||||
|
||||
/* initialise codecs */
|
||||
ice->num_total_dacs = 4;
|
||||
ice->num_total_adcs = 4;
|
||||
ice->akm_codecs = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
wm8776_init(ice, &chip->wm[i], wm8776_addr[i]);
|
||||
wm8776_select_input(chip, i, MAYA_LINE_IN);
|
||||
}
|
||||
|
||||
/* set card specific rates */
|
||||
ice->hw_rates = &dac_rates;
|
||||
|
||||
/* register change rate notifier */
|
||||
ice->gpio.set_pro_rate = set_rate;
|
||||
|
||||
/* RDMA1 (2nd input channel) is used for ADC by default */
|
||||
ice->force_rdma1 = 1;
|
||||
|
||||
/* have an own routing control */
|
||||
ice->own_routing = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Maya44 boards don't provide the EEPROM data except for the vendor IDs.
|
||||
* hence the driver needs to sets up it properly.
|
||||
*/
|
||||
|
||||
static unsigned char maya44_eeprom[] __devinitdata = {
|
||||
[ICE_EEP2_SYSCONF] = 0x45,
|
||||
/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
|
||||
[ICE_EEP2_ACLINK] = 0x80,
|
||||
/* I2S */
|
||||
[ICE_EEP2_I2S] = 0xf8,
|
||||
/* vol, 96k, 24bit, 192k */
|
||||
[ICE_EEP2_SPDIF] = 0xc3,
|
||||
/* enable spdif out, spdif out supp, spdif-in, ext spdif out */
|
||||
[ICE_EEP2_GPIO_DIR] = 0xff,
|
||||
[ICE_EEP2_GPIO_DIR1] = 0xff,
|
||||
[ICE_EEP2_GPIO_DIR2] = 0xff,
|
||||
[ICE_EEP2_GPIO_MASK] = 0/*0x9f*/,
|
||||
[ICE_EEP2_GPIO_MASK1] = 0/*0xff*/,
|
||||
[ICE_EEP2_GPIO_MASK2] = 0/*0x7f*/,
|
||||
[ICE_EEP2_GPIO_STATE] = (1 << GPIO_PHANTOM_OFF) |
|
||||
(1 << GPIO_SPDIF_IN_INV),
|
||||
[ICE_EEP2_GPIO_STATE1] = 0x00,
|
||||
[ICE_EEP2_GPIO_STATE2] = 0x00,
|
||||
};
|
||||
|
||||
/* entry point */
|
||||
struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
|
||||
{
|
||||
.subvendor = VT1724_SUBDEVICE_MAYA44,
|
||||
.name = "ESI Maya44",
|
||||
.model = "maya44",
|
||||
.chip_init = maya44_init,
|
||||
.build_controls = maya44_add_controls,
|
||||
.eeprom_size = sizeof(maya44_eeprom),
|
||||
.eeprom_data = maya44_eeprom,
|
||||
},
|
||||
{ } /* terminator */
|
||||
};
|
10
sound/pci/ice1712/maya44.h
Normal file
10
sound/pci/ice1712/maya44.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __SOUND_MAYA44_H
|
||||
#define __SOUND_MAYA44_H
|
||||
|
||||
#define MAYA44_DEVICE_DESC "{ESI,Maya44},"
|
||||
|
||||
#define VT1724_SUBDEVICE_MAYA44 0x34315441 /* Maya44 */
|
||||
|
||||
extern struct snd_ice1712_card_info snd_vt1724_maya44_cards[];
|
||||
|
||||
#endif /* __SOUND_MAYA44_H */
|
2
sound/pci/lx6464es/Makefile
Normal file
2
sound/pci/lx6464es/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
snd-lx6464es-objs := lx6464es.o lx_core.o
|
||||
obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
|
1159
sound/pci/lx6464es/lx6464es.c
Normal file
1159
sound/pci/lx6464es/lx6464es.c
Normal file
File diff suppressed because it is too large
Load diff
114
sound/pci/lx6464es/lx6464es.h
Normal file
114
sound/pci/lx6464es/lx6464es.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* -*- linux-c -*- *
|
||||
*
|
||||
* ALSA driver for the digigram lx6464es interface
|
||||
*
|
||||
* Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LX6464ES_H
|
||||
#define LX6464ES_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include "lx_core.h"
|
||||
|
||||
#define LXP "LX6464ES: "
|
||||
|
||||
enum {
|
||||
ES_cmd_free = 0, /* no command executing */
|
||||
ES_cmd_processing = 1, /* execution of a read/write command */
|
||||
ES_read_pending = 2, /* a asynchron read command is pending */
|
||||
ES_read_finishing = 3, /* a read command has finished waiting (set by
|
||||
* Interrupt or CancelIrp) */
|
||||
};
|
||||
|
||||
enum lx_stream_status {
|
||||
LX_STREAM_STATUS_FREE,
|
||||
/* LX_STREAM_STATUS_OPEN, */
|
||||
LX_STREAM_STATUS_SCHEDULE_RUN,
|
||||
/* LX_STREAM_STATUS_STARTED, */
|
||||
LX_STREAM_STATUS_RUNNING,
|
||||
LX_STREAM_STATUS_SCHEDULE_STOP,
|
||||
/* LX_STREAM_STATUS_STOPPED, */
|
||||
/* LX_STREAM_STATUS_PAUSED */
|
||||
};
|
||||
|
||||
|
||||
struct lx_stream {
|
||||
struct snd_pcm_substream *stream;
|
||||
snd_pcm_uframes_t frame_pos;
|
||||
enum lx_stream_status status; /* free, open, running, draining
|
||||
* pause */
|
||||
int is_capture:1;
|
||||
};
|
||||
|
||||
|
||||
struct lx6464es {
|
||||
struct snd_card *card;
|
||||
struct pci_dev *pci;
|
||||
int irq;
|
||||
|
||||
spinlock_t lock; /* interrupt spinlock */
|
||||
struct mutex setup_mutex; /* mutex used in hw_params, open
|
||||
* and close */
|
||||
|
||||
struct tasklet_struct trigger_tasklet; /* trigger tasklet */
|
||||
struct tasklet_struct tasklet_capture;
|
||||
struct tasklet_struct tasklet_playback;
|
||||
|
||||
/* ports */
|
||||
unsigned long port_plx; /* io port (size=256) */
|
||||
void __iomem *port_plx_remapped; /* remapped plx port */
|
||||
void __iomem *port_dsp_bar; /* memory port (32-bit,
|
||||
* non-prefetchable,
|
||||
* size=8K) */
|
||||
|
||||
/* messaging */
|
||||
spinlock_t msg_lock; /* message spinlock */
|
||||
atomic_t send_message_locked;
|
||||
struct lx_rmh rmh;
|
||||
|
||||
/* configuration */
|
||||
uint freq_ratio : 2;
|
||||
uint playback_mute : 1;
|
||||
uint hardware_running[2];
|
||||
u32 board_sample_rate; /* sample rate read from
|
||||
* board */
|
||||
u32 sample_rate; /* our sample rate */
|
||||
u16 pcm_granularity; /* board blocksize */
|
||||
|
||||
/* dma */
|
||||
struct snd_dma_buffer capture_dma_buf;
|
||||
struct snd_dma_buffer playback_dma_buf;
|
||||
|
||||
/* pcm */
|
||||
struct snd_pcm *pcm;
|
||||
|
||||
/* streams */
|
||||
struct lx_stream capture_stream;
|
||||
struct lx_stream playback_stream;
|
||||
};
|
||||
|
||||
|
||||
#endif /* LX6464ES_H */
|
1444
sound/pci/lx6464es/lx_core.c
Normal file
1444
sound/pci/lx6464es/lx_core.c
Normal file
File diff suppressed because it is too large
Load diff
242
sound/pci/lx6464es/lx_core.h
Normal file
242
sound/pci/lx6464es/lx_core.h
Normal file
|
@ -0,0 +1,242 @@
|
|||
/* -*- linux-c -*- *
|
||||
*
|
||||
* ALSA driver for the digigram lx6464es interface
|
||||
* low-level interface
|
||||
*
|
||||
* Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LX_CORE_H
|
||||
#define LX_CORE_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "lx_defs.h"
|
||||
|
||||
#define REG_CRM_NUMBER 12
|
||||
|
||||
struct lx6464es;
|
||||
|
||||
/* low-level register access */
|
||||
|
||||
/* dsp register access */
|
||||
enum {
|
||||
eReg_BASE,
|
||||
eReg_CSM,
|
||||
eReg_CRM1,
|
||||
eReg_CRM2,
|
||||
eReg_CRM3,
|
||||
eReg_CRM4,
|
||||
eReg_CRM5,
|
||||
eReg_CRM6,
|
||||
eReg_CRM7,
|
||||
eReg_CRM8,
|
||||
eReg_CRM9,
|
||||
eReg_CRM10,
|
||||
eReg_CRM11,
|
||||
eReg_CRM12,
|
||||
|
||||
eReg_ICR,
|
||||
eReg_CVR,
|
||||
eReg_ISR,
|
||||
eReg_RXHTXH,
|
||||
eReg_RXMTXM,
|
||||
eReg_RHLTXL,
|
||||
eReg_RESETDSP,
|
||||
|
||||
eReg_CSUF,
|
||||
eReg_CSES,
|
||||
eReg_CRESMSB,
|
||||
eReg_CRESLSB,
|
||||
eReg_ADMACESMSB,
|
||||
eReg_ADMACESLSB,
|
||||
eReg_CONFES,
|
||||
|
||||
eMaxPortLx
|
||||
};
|
||||
|
||||
unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port);
|
||||
void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len);
|
||||
void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data);
|
||||
void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
|
||||
u32 len);
|
||||
|
||||
/* plx register access */
|
||||
enum {
|
||||
ePLX_PCICR,
|
||||
|
||||
ePLX_MBOX0,
|
||||
ePLX_MBOX1,
|
||||
ePLX_MBOX2,
|
||||
ePLX_MBOX3,
|
||||
ePLX_MBOX4,
|
||||
ePLX_MBOX5,
|
||||
ePLX_MBOX6,
|
||||
ePLX_MBOX7,
|
||||
|
||||
ePLX_L2PCIDB,
|
||||
ePLX_IRQCS,
|
||||
ePLX_CHIPSC,
|
||||
|
||||
eMaxPort
|
||||
};
|
||||
|
||||
unsigned long lx_plx_reg_read(struct lx6464es *chip, int port);
|
||||
void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data);
|
||||
|
||||
/* rhm */
|
||||
struct lx_rmh {
|
||||
u16 cmd_len; /* length of the command to send (WORDs) */
|
||||
u16 stat_len; /* length of the status received (WORDs) */
|
||||
u16 dsp_stat; /* status type, RMP_SSIZE_XXX */
|
||||
u16 cmd_idx; /* index of the command */
|
||||
u32 cmd[REG_CRM_NUMBER];
|
||||
u32 stat[REG_CRM_NUMBER];
|
||||
};
|
||||
|
||||
|
||||
/* low-level dsp access */
|
||||
int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
|
||||
int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq);
|
||||
int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran);
|
||||
int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data);
|
||||
int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address);
|
||||
|
||||
|
||||
/* low-level pipe handling */
|
||||
int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
int channels);
|
||||
int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u64 *rsample_count);
|
||||
int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate);
|
||||
int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
|
||||
int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture);
|
||||
|
||||
/* low-level stream handling */
|
||||
int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
|
||||
u32 pipe, int is_capture);
|
||||
int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
int *rstate);
|
||||
int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u64 *r_bytepos);
|
||||
|
||||
int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
|
||||
int is_capture, enum stream_state_t state);
|
||||
|
||||
static inline int lx_stream_start(struct lx6464es *chip, u32 pipe,
|
||||
int is_capture)
|
||||
{
|
||||
snd_printdd("->lx_stream_start\n");
|
||||
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN);
|
||||
}
|
||||
|
||||
static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe,
|
||||
int is_capture)
|
||||
{
|
||||
snd_printdd("->lx_stream_pause\n");
|
||||
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE);
|
||||
}
|
||||
|
||||
static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe,
|
||||
int is_capture)
|
||||
{
|
||||
snd_printdd("->lx_stream_stop\n");
|
||||
return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP);
|
||||
}
|
||||
|
||||
/* low-level buffer handling */
|
||||
int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u32 *r_needed, u32 *r_freed, u32 *size_array);
|
||||
int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
|
||||
u32 *r_buffer_index);
|
||||
int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u32 *r_buffer_size);
|
||||
int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
|
||||
u32 buffer_index);
|
||||
|
||||
/* low-level gain/peak handling */
|
||||
int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute);
|
||||
int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
|
||||
u32 *r_levels);
|
||||
|
||||
|
||||
/* interrupt handling */
|
||||
irqreturn_t lx_interrupt(int irq, void *dev_id);
|
||||
void lx_irq_enable(struct lx6464es *chip);
|
||||
void lx_irq_disable(struct lx6464es *chip);
|
||||
|
||||
void lx_tasklet_capture(unsigned long data);
|
||||
void lx_tasklet_playback(unsigned long data);
|
||||
|
||||
|
||||
/* Stream Format Header Defines (for LIN and IEEE754) */
|
||||
#define HEADER_FMT_BASE HEADER_FMT_BASE_LIN
|
||||
#define HEADER_FMT_BASE_LIN 0xFED00000
|
||||
#define HEADER_FMT_BASE_FLOAT 0xFAD00000
|
||||
#define HEADER_FMT_MONO 0x00000080 /* bit 23 in header_lo. WARNING: old
|
||||
* bit 22 is ignored in float
|
||||
* format */
|
||||
#define HEADER_FMT_INTEL 0x00008000
|
||||
#define HEADER_FMT_16BITS 0x00002000
|
||||
#define HEADER_FMT_24BITS 0x00004000
|
||||
#define HEADER_FMT_UPTO11 0x00000200 /* frequency is less or equ. to 11k.
|
||||
* */
|
||||
#define HEADER_FMT_UPTO32 0x00000100 /* frequency is over 11k and less
|
||||
* then 32k.*/
|
||||
|
||||
|
||||
#define BIT_FMP_HEADER 23
|
||||
#define BIT_FMP_SD 22
|
||||
#define BIT_FMP_MULTICHANNEL 19
|
||||
|
||||
#define START_STATE 1
|
||||
#define PAUSE_STATE 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* from PcxAll_e.h */
|
||||
/* Start/Pause condition for pipes (PCXStartPipe, PCXPausePipe) */
|
||||
#define START_PAUSE_IMMEDIATE 0
|
||||
#define START_PAUSE_ON_SYNCHRO 1
|
||||
#define START_PAUSE_ON_TIME_CODE 2
|
||||
|
||||
|
||||
/* Pipe / Stream state */
|
||||
#define START_STATE 1
|
||||
#define PAUSE_STATE 0
|
||||
|
||||
static inline void unpack_pointer(dma_addr_t ptr, u32 *r_low, u32 *r_high)
|
||||
{
|
||||
*r_low = (u32)(ptr & 0xffffffff);
|
||||
#if BITS_PER_LONG == 32
|
||||
*r_high = 0;
|
||||
#else
|
||||
*r_high = (u32)((u64)ptr>>32);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* LX_CORE_H */
|
376
sound/pci/lx6464es/lx_defs.h
Normal file
376
sound/pci/lx6464es/lx_defs.h
Normal file
|
@ -0,0 +1,376 @@
|
|||
/* -*- linux-c -*- *
|
||||
*
|
||||
* ALSA driver for the digigram lx6464es interface
|
||||
* adapted upstream headers
|
||||
*
|
||||
* Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LX_DEFS_H
|
||||
#define LX_DEFS_H
|
||||
|
||||
/* code adapted from ethersound.h */
|
||||
#define XES_FREQ_COUNT8_MASK 0x00001FFF /* compteur 25MHz entre 8 ech. */
|
||||
#define XES_FREQ_COUNT8_44_MIN 0x00001288 /* 25M /
|
||||
* [ 44k - ( 44.1k + 48k ) / 2 ]
|
||||
* * 8 */
|
||||
#define XES_FREQ_COUNT8_44_MAX 0x000010F0 /* 25M / [ ( 44.1k + 48k ) / 2 ]
|
||||
* * 8 */
|
||||
#define XES_FREQ_COUNT8_48_MAX 0x00000F08 /* 25M /
|
||||
* [ 48k + ( 44.1k + 48k ) / 2 ]
|
||||
* * 8 */
|
||||
|
||||
/* code adapted from LXES_registers.h */
|
||||
|
||||
#define IOCR_OUTPUTS_OFFSET 0 /* (rw) offset for the number of OUTs in the
|
||||
* ConfES register. */
|
||||
#define IOCR_INPUTS_OFFSET 8 /* (rw) offset for the number of INs in the
|
||||
* ConfES register. */
|
||||
#define FREQ_RATIO_OFFSET 19 /* (rw) offset for frequency ratio in the
|
||||
* ConfES register. */
|
||||
#define FREQ_RATIO_SINGLE_MODE 0x01 /* value for single mode frequency ratio:
|
||||
* sample rate = frequency rate. */
|
||||
|
||||
#define CONFES_READ_PART_MASK 0x00070000
|
||||
#define CONFES_WRITE_PART_MASK 0x00F80000
|
||||
|
||||
/* code adapted from if_drv_mb.h */
|
||||
|
||||
#define MASK_SYS_STATUS_ERROR (1L << 31) /* events that lead to a PCI irq if
|
||||
* not yet pending */
|
||||
#define MASK_SYS_STATUS_URUN (1L << 30)
|
||||
#define MASK_SYS_STATUS_ORUN (1L << 29)
|
||||
#define MASK_SYS_STATUS_EOBO (1L << 28)
|
||||
#define MASK_SYS_STATUS_EOBI (1L << 27)
|
||||
#define MASK_SYS_STATUS_FREQ (1L << 26)
|
||||
#define MASK_SYS_STATUS_ESA (1L << 25) /* reserved, this is set by the
|
||||
* XES */
|
||||
#define MASK_SYS_STATUS_TIMER (1L << 24)
|
||||
|
||||
#define MASK_SYS_ASYNC_EVENTS (MASK_SYS_STATUS_ERROR | \
|
||||
MASK_SYS_STATUS_URUN | \
|
||||
MASK_SYS_STATUS_ORUN | \
|
||||
MASK_SYS_STATUS_EOBO | \
|
||||
MASK_SYS_STATUS_EOBI | \
|
||||
MASK_SYS_STATUS_FREQ | \
|
||||
MASK_SYS_STATUS_ESA)
|
||||
|
||||
#define MASK_SYS_PCI_EVENTS (MASK_SYS_ASYNC_EVENTS | \
|
||||
MASK_SYS_STATUS_TIMER)
|
||||
|
||||
#define MASK_SYS_TIMER_COUNT 0x0000FFFF
|
||||
|
||||
#define MASK_SYS_STATUS_EOT_PLX (1L << 22) /* event that remains
|
||||
* internal: reserved fo end
|
||||
* of plx dma */
|
||||
#define MASK_SYS_STATUS_XES (1L << 21) /* event that remains
|
||||
* internal: pending XES
|
||||
* IRQ */
|
||||
#define MASK_SYS_STATUS_CMD_DONE (1L << 20) /* alternate command
|
||||
* management: notify driver
|
||||
* instead of polling */
|
||||
|
||||
|
||||
#define MAX_STREAM_BUFFER 5 /* max amount of stream buffers. */
|
||||
|
||||
#define MICROBLAZE_IBL_MIN 32
|
||||
#define MICROBLAZE_IBL_DEFAULT 128
|
||||
#define MICROBLAZE_IBL_MAX 512
|
||||
/* #define MASK_GRANULARITY (2*MICROBLAZE_IBL_MAX-1) */
|
||||
|
||||
|
||||
|
||||
/* command opcodes, see reference for details */
|
||||
|
||||
/*
|
||||
the capture bit position in the object_id field in driver commands
|
||||
depends upon the number of managed channels. For now, 64 IN + 64 OUT are
|
||||
supported. HOwever, the communication protocol forsees 1024 channels, hence
|
||||
bit 10 indicates a capture (input) object).
|
||||
*/
|
||||
#define ID_IS_CAPTURE (1L << 10)
|
||||
#define ID_OFFSET 13 /* object ID is at the 13th bit in the
|
||||
* 1st command word.*/
|
||||
#define ID_CH_MASK 0x3F
|
||||
#define OPCODE_OFFSET 24 /* offset of the command opcode in the first
|
||||
* command word.*/
|
||||
|
||||
enum cmd_mb_opcodes {
|
||||
CMD_00_INFO_DEBUG = 0x00,
|
||||
CMD_01_GET_SYS_CFG = 0x01,
|
||||
CMD_02_SET_GRANULARITY = 0x02,
|
||||
CMD_03_SET_TIMER_IRQ = 0x03,
|
||||
CMD_04_GET_EVENT = 0x04,
|
||||
CMD_05_GET_PIPES = 0x05,
|
||||
|
||||
CMD_06_ALLOCATE_PIPE = 0x06,
|
||||
CMD_07_RELEASE_PIPE = 0x07,
|
||||
CMD_08_ASK_BUFFERS = 0x08,
|
||||
CMD_09_STOP_PIPE = 0x09,
|
||||
CMD_0A_GET_PIPE_SPL_COUNT = 0x0a,
|
||||
CMD_0B_TOGGLE_PIPE_STATE = 0x0b,
|
||||
|
||||
CMD_0C_DEF_STREAM = 0x0c,
|
||||
CMD_0D_SET_MUTE = 0x0d,
|
||||
CMD_0E_GET_STREAM_SPL_COUNT = 0x0e,
|
||||
CMD_0F_UPDATE_BUFFER = 0x0f,
|
||||
CMD_10_GET_BUFFER = 0x10,
|
||||
CMD_11_CANCEL_BUFFER = 0x11,
|
||||
CMD_12_GET_PEAK = 0x12,
|
||||
CMD_13_SET_STREAM_STATE = 0x13,
|
||||
CMD_14_INVALID = 0x14,
|
||||
};
|
||||
|
||||
/* pipe states */
|
||||
enum pipe_state_t {
|
||||
PSTATE_IDLE = 0, /* the pipe is not processed in the XES_IRQ
|
||||
* (free or stopped, or paused). */
|
||||
PSTATE_RUN = 1, /* sustained play/record state. */
|
||||
PSTATE_PURGE = 2, /* the ES channels are now off, render pipes do
|
||||
* not DMA, record pipe do a last DMA. */
|
||||
PSTATE_ACQUIRE = 3, /* the ES channels are now on, render pipes do
|
||||
* not yet increase their sample count, record
|
||||
* pipes do not DMA. */
|
||||
PSTATE_CLOSING = 4, /* the pipe is releasing, and may not yet
|
||||
* receive an "alloc" command. */
|
||||
};
|
||||
|
||||
/* stream states */
|
||||
enum stream_state_t {
|
||||
SSTATE_STOP = 0x00, /* setting to stop resets the stream spl
|
||||
* count.*/
|
||||
SSTATE_RUN = (0x01 << 0), /* start DMA and spl count handling. */
|
||||
SSTATE_PAUSE = (0x01 << 1), /* pause DMA and spl count handling. */
|
||||
};
|
||||
|
||||
/* buffer flags */
|
||||
enum buffer_flags {
|
||||
BF_VALID = 0x80, /* set if the buffer is valid, clear if free.*/
|
||||
BF_CURRENT = 0x40, /* set if this is the current buffer (there is
|
||||
* always a current buffer).*/
|
||||
BF_NOTIFY_EOB = 0x20, /* set if this buffer must cause a PCI event
|
||||
* when finished.*/
|
||||
BF_CIRCULAR = 0x10, /* set if buffer[1] must be copied to buffer[0]
|
||||
* by the end of this buffer.*/
|
||||
BF_64BITS_ADR = 0x08, /* set if the hi part of the address is valid.*/
|
||||
BF_xx = 0x04, /* future extension.*/
|
||||
BF_EOB = 0x02, /* set if finished, but not yet free.*/
|
||||
BF_PAUSE = 0x01, /* pause stream at buffer end.*/
|
||||
BF_ZERO = 0x00, /* no flags (init).*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Stream Flags definitions
|
||||
*/
|
||||
enum stream_flags {
|
||||
SF_ZERO = 0x00000000, /* no flags (stream invalid). */
|
||||
SF_VALID = 0x10000000, /* the stream has a valid DMA_conf
|
||||
* info (setstreamformat). */
|
||||
SF_XRUN = 0x20000000, /* the stream is un x-run state. */
|
||||
SF_START = 0x40000000, /* the DMA is running.*/
|
||||
SF_ASIO = 0x80000000, /* ASIO.*/
|
||||
};
|
||||
|
||||
|
||||
#define MASK_SPL_COUNT_HI 0x00FFFFFF /* 4 MSBits are status bits */
|
||||
#define PSTATE_OFFSET 28 /* 4 MSBits are status bits */
|
||||
|
||||
|
||||
#define MASK_STREAM_HAS_MAPPING (1L << 12)
|
||||
#define MASK_STREAM_IS_ASIO (1L << 9)
|
||||
#define STREAM_FMT_OFFSET 10 /* the stream fmt bits start at the 10th
|
||||
* bit in the command word. */
|
||||
|
||||
#define STREAM_FMT_16b 0x02
|
||||
#define STREAM_FMT_intel 0x01
|
||||
|
||||
#define FREQ_FIELD_OFFSET 15 /* offset of the freq field in the response
|
||||
* word */
|
||||
|
||||
#define BUFF_FLAGS_OFFSET 24 /* offset of the buffer flags in the
|
||||
* response word. */
|
||||
#define MASK_DATA_SIZE 0x00FFFFFF /* this must match the field size of
|
||||
* datasize in the buffer_t structure. */
|
||||
|
||||
#define MASK_BUFFER_ID 0xFF /* the cancel command awaits a buffer ID,
|
||||
* may be 0xFF for "current". */
|
||||
|
||||
|
||||
/* code adapted from PcxErr_e.h */
|
||||
|
||||
/* Bits masks */
|
||||
|
||||
#define ERROR_MASK 0x8000
|
||||
|
||||
#define SOURCE_MASK 0x7800
|
||||
|
||||
#define E_SOURCE_BOARD 0x4000 /* 8 >> 1 */
|
||||
#define E_SOURCE_DRV 0x2000 /* 4 >> 1 */
|
||||
#define E_SOURCE_API 0x1000 /* 2 >> 1 */
|
||||
/* Error tools */
|
||||
#define E_SOURCE_TOOLS 0x0800 /* 1 >> 1 */
|
||||
/* Error pcxaudio */
|
||||
#define E_SOURCE_AUDIO 0x1800 /* 3 >> 1 */
|
||||
/* Error virtual pcx */
|
||||
#define E_SOURCE_VPCX 0x2800 /* 5 >> 1 */
|
||||
/* Error dispatcher */
|
||||
#define E_SOURCE_DISPATCHER 0x3000 /* 6 >> 1 */
|
||||
/* Error from CobraNet firmware */
|
||||
#define E_SOURCE_COBRANET 0x3800 /* 7 >> 1 */
|
||||
|
||||
#define E_SOURCE_USER 0x7800
|
||||
|
||||
#define CLASS_MASK 0x0700
|
||||
|
||||
#define CODE_MASK 0x00FF
|
||||
|
||||
/* Bits values */
|
||||
|
||||
/* Values for the error/warning bit */
|
||||
#define ERROR_VALUE 0x8000
|
||||
#define WARNING_VALUE 0x0000
|
||||
|
||||
/* Class values */
|
||||
#define E_CLASS_GENERAL 0x0000
|
||||
#define E_CLASS_INVALID_CMD 0x0100
|
||||
#define E_CLASS_INVALID_STD_OBJECT 0x0200
|
||||
#define E_CLASS_RSRC_IMPOSSIBLE 0x0300
|
||||
#define E_CLASS_WRONG_CONTEXT 0x0400
|
||||
#define E_CLASS_BAD_SPECIFIC_PARAMETER 0x0500
|
||||
#define E_CLASS_REAL_TIME_ERROR 0x0600
|
||||
#define E_CLASS_DIRECTSHOW 0x0700
|
||||
#define E_CLASS_FREE 0x0700
|
||||
|
||||
|
||||
/* Complete DRV error code for the general class */
|
||||
#define ED_GN (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_GENERAL)
|
||||
#define ED_CONCURRENCY (ED_GN | 0x01)
|
||||
#define ED_DSP_CRASHED (ED_GN | 0x02)
|
||||
#define ED_UNKNOWN_BOARD (ED_GN | 0x03)
|
||||
#define ED_NOT_INSTALLED (ED_GN | 0x04)
|
||||
#define ED_CANNOT_OPEN_SVC_MANAGER (ED_GN | 0x05)
|
||||
#define ED_CANNOT_READ_REGISTRY (ED_GN | 0x06)
|
||||
#define ED_DSP_VERSION_MISMATCH (ED_GN | 0x07)
|
||||
#define ED_UNAVAILABLE_FEATURE (ED_GN | 0x08)
|
||||
#define ED_CANCELLED (ED_GN | 0x09)
|
||||
#define ED_NO_RESPONSE_AT_IRQA (ED_GN | 0x10)
|
||||
#define ED_INVALID_ADDRESS (ED_GN | 0x11)
|
||||
#define ED_DSP_CORRUPTED (ED_GN | 0x12)
|
||||
#define ED_PENDING_OPERATION (ED_GN | 0x13)
|
||||
#define ED_NET_ALLOCATE_MEMORY_IMPOSSIBLE (ED_GN | 0x14)
|
||||
#define ED_NET_REGISTER_ERROR (ED_GN | 0x15)
|
||||
#define ED_NET_THREAD_ERROR (ED_GN | 0x16)
|
||||
#define ED_NET_OPEN_ERROR (ED_GN | 0x17)
|
||||
#define ED_NET_CLOSE_ERROR (ED_GN | 0x18)
|
||||
#define ED_NET_NO_MORE_PACKET (ED_GN | 0x19)
|
||||
#define ED_NET_NO_MORE_BUFFER (ED_GN | 0x1A)
|
||||
#define ED_NET_SEND_ERROR (ED_GN | 0x1B)
|
||||
#define ED_NET_RECEIVE_ERROR (ED_GN | 0x1C)
|
||||
#define ED_NET_WRONG_MSG_SIZE (ED_GN | 0x1D)
|
||||
#define ED_NET_WAIT_ERROR (ED_GN | 0x1E)
|
||||
#define ED_NET_EEPROM_ERROR (ED_GN | 0x1F)
|
||||
#define ED_INVALID_RS232_COM_NUMBER (ED_GN | 0x20)
|
||||
#define ED_INVALID_RS232_INIT (ED_GN | 0x21)
|
||||
#define ED_FILE_ERROR (ED_GN | 0x22)
|
||||
#define ED_INVALID_GPIO_CMD (ED_GN | 0x23)
|
||||
#define ED_RS232_ALREADY_OPENED (ED_GN | 0x24)
|
||||
#define ED_RS232_NOT_OPENED (ED_GN | 0x25)
|
||||
#define ED_GPIO_ALREADY_OPENED (ED_GN | 0x26)
|
||||
#define ED_GPIO_NOT_OPENED (ED_GN | 0x27)
|
||||
#define ED_REGISTRY_ERROR (ED_GN | 0x28) /* <- NCX */
|
||||
#define ED_INVALID_SERVICE (ED_GN | 0x29) /* <- NCX */
|
||||
|
||||
#define ED_READ_FILE_ALREADY_OPENED (ED_GN | 0x2a) /* <- Decalage
|
||||
* pour RCX
|
||||
* (old 0x28)
|
||||
* */
|
||||
#define ED_READ_FILE_INVALID_COMMAND (ED_GN | 0x2b) /* ~ */
|
||||
#define ED_READ_FILE_INVALID_PARAMETER (ED_GN | 0x2c) /* ~ */
|
||||
#define ED_READ_FILE_ALREADY_CLOSED (ED_GN | 0x2d) /* ~ */
|
||||
#define ED_READ_FILE_NO_INFORMATION (ED_GN | 0x2e) /* ~ */
|
||||
#define ED_READ_FILE_INVALID_HANDLE (ED_GN | 0x2f) /* ~ */
|
||||
#define ED_READ_FILE_END_OF_FILE (ED_GN | 0x30) /* ~ */
|
||||
#define ED_READ_FILE_ERROR (ED_GN | 0x31) /* ~ */
|
||||
|
||||
#define ED_DSP_CRASHED_EXC_DSPSTACK_OVERFLOW (ED_GN | 0x32) /* <- Decalage pour
|
||||
* PCX (old 0x14) */
|
||||
#define ED_DSP_CRASHED_EXC_SYSSTACK_OVERFLOW (ED_GN | 0x33) /* ~ */
|
||||
#define ED_DSP_CRASHED_EXC_ILLEGAL (ED_GN | 0x34) /* ~ */
|
||||
#define ED_DSP_CRASHED_EXC_TIMER_REENTRY (ED_GN | 0x35) /* ~ */
|
||||
#define ED_DSP_CRASHED_EXC_FATAL_ERROR (ED_GN | 0x36) /* ~ */
|
||||
|
||||
#define ED_FLASH_PCCARD_NOT_PRESENT (ED_GN | 0x37)
|
||||
|
||||
#define ED_NO_CURRENT_CLOCK (ED_GN | 0x38)
|
||||
|
||||
/* Complete DRV error code for real time class */
|
||||
#define ED_RT (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_REAL_TIME_ERROR)
|
||||
#define ED_DSP_TIMED_OUT (ED_RT | 0x01)
|
||||
#define ED_DSP_CHK_TIMED_OUT (ED_RT | 0x02)
|
||||
#define ED_STREAM_OVERRUN (ED_RT | 0x03)
|
||||
#define ED_DSP_BUSY (ED_RT | 0x04)
|
||||
#define ED_DSP_SEMAPHORE_TIME_OUT (ED_RT | 0x05)
|
||||
#define ED_BOARD_TIME_OUT (ED_RT | 0x06)
|
||||
#define ED_XILINX_ERROR (ED_RT | 0x07)
|
||||
#define ED_COBRANET_ITF_NOT_RESPONDING (ED_RT | 0x08)
|
||||
|
||||
/* Complete BOARD error code for the invaid standard object class */
|
||||
#define EB_ISO (ERROR_VALUE | E_SOURCE_BOARD | \
|
||||
E_CLASS_INVALID_STD_OBJECT)
|
||||
#define EB_INVALID_EFFECT (EB_ISO | 0x00)
|
||||
#define EB_INVALID_PIPE (EB_ISO | 0x40)
|
||||
#define EB_INVALID_STREAM (EB_ISO | 0x80)
|
||||
#define EB_INVALID_AUDIO (EB_ISO | 0xC0)
|
||||
|
||||
/* Complete BOARD error code for impossible resource allocation class */
|
||||
#define EB_RI (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_RSRC_IMPOSSIBLE)
|
||||
#define EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE (EB_RI | 0x01)
|
||||
#define EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE (EB_RI | 0x02)
|
||||
|
||||
#define EB_ALLOCATE_MEM_STREAM_IMPOSSIBLE \
|
||||
EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE
|
||||
#define EB_ALLOCATE_MEM_PIPE_IMPOSSIBLE \
|
||||
EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE
|
||||
|
||||
#define EB_ALLOCATE_DIFFERED_CMD_IMPOSSIBLE (EB_RI | 0x03)
|
||||
#define EB_TOO_MANY_DIFFERED_CMD (EB_RI | 0x04)
|
||||
#define EB_RBUFFERS_TABLE_OVERFLOW (EB_RI | 0x05)
|
||||
#define EB_ALLOCATE_EFFECTS_IMPOSSIBLE (EB_RI | 0x08)
|
||||
#define EB_ALLOCATE_EFFECT_POS_IMPOSSIBLE (EB_RI | 0x09)
|
||||
#define EB_RBUFFER_NOT_AVAILABLE (EB_RI | 0x0A)
|
||||
#define EB_ALLOCATE_CONTEXT_LIII_IMPOSSIBLE (EB_RI | 0x0B)
|
||||
#define EB_STATUS_DIALOG_IMPOSSIBLE (EB_RI | 0x1D)
|
||||
#define EB_CONTROL_CMD_IMPOSSIBLE (EB_RI | 0x1E)
|
||||
#define EB_STATUS_SEND_IMPOSSIBLE (EB_RI | 0x1F)
|
||||
#define EB_ALLOCATE_PIPE_IMPOSSIBLE (EB_RI | 0x40)
|
||||
#define EB_ALLOCATE_STREAM_IMPOSSIBLE (EB_RI | 0x80)
|
||||
#define EB_ALLOCATE_AUDIO_IMPOSSIBLE (EB_RI | 0xC0)
|
||||
|
||||
/* Complete BOARD error code for wrong call context class */
|
||||
#define EB_WCC (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_WRONG_CONTEXT)
|
||||
#define EB_CMD_REFUSED (EB_WCC | 0x00)
|
||||
#define EB_START_STREAM_REFUSED (EB_WCC | 0xFC)
|
||||
#define EB_SPC_REFUSED (EB_WCC | 0xFD)
|
||||
#define EB_CSN_REFUSED (EB_WCC | 0xFE)
|
||||
#define EB_CSE_REFUSED (EB_WCC | 0xFF)
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* LX_DEFS_H */
|
|
@ -487,10 +487,14 @@ static int oxygen_hw_free(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
unsigned int channel_mask = 1 << channel;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->interrupt_mask &= ~(1 << channel);
|
||||
chip->interrupt_mask &= ~channel_mask;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
|
||||
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue