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:
Linus Torvalds 2009-06-12 11:16:27 -07:00
commit e349792a38
213 changed files with 33620 additions and 4038 deletions

View File

@ -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.

View File

@ -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*

View File

@ -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.

View 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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -1 +0,0 @@
#warning "This file is deprecated"

View File

@ -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
*/

View File

@ -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;
};

View File

@ -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

View File

@ -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
View 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

View File

@ -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);

View File

@ -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;

View File

@ -205,3 +205,5 @@ config SND_PCM_XRUN_DEBUG
config SND_VMASTER
bool
source "sound/core/seq/Kconfig"

View File

@ -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);

View File

@ -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)

View File

@ -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
}

View File

@ -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;
}

View File

@ -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(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
snd_mask_max(&params->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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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 @@ err_exit:
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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -59,9 +59,11 @@ obj-$(CONFIG_SND) += \
ali5451/ \
au88x0/ \
aw2/ \
ctxfi/ \
ca0106/ \
cs46xx/ \
cs5535audio/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
hda/ \

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -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
View 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
View 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 */

View 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
View 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;
}

View 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

File diff suppressed because it is too large Load Diff

147
sound/pci/ctxfi/ctatc.h Normal file
View 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
View 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
View 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 */

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

67
sound/pci/ctxfi/ctmixer.h Normal file
View 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
View 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
View 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 */

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View File

@ -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

View File

@ -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");

View File

@ -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,
};

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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 */
};

View File

@ -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;

View File

@ -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

View File

@ -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),

View File

@ -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,

View File

@ -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);

View 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)

View File

@ -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

View File

@ -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 @@ again:
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 @@ again:
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 @@ again:
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 @@ again:
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 },

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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
View 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 */
};

View 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 */

View File

@ -0,0 +1,2 @@
snd-lx6464es-objs := lx6464es.o lx_core.o
obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View File

@ -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