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: (250 commits) [ALSA] ice1724 - Fix IRQ lock-up with MPU access [ALSA] Define MPU401 registers in sound/mpu401_uart.h [ALSA] pcsp: fix wording in DEBUG_PAGEALLOC warning [ALSA] pcsp - Fix dependency in Kconfig [ALSA] soc - ac97 - Clean up checkpatch warnings [ALSA] soc - wm8750 - Clean up checkpatch warnings [ALSA] soc - wm8731 - Clean up checkpatch warnings [ALSA] soc - pxa2xx-pcm - Fix checkpatch warnings [ALSA] soc - spitz - Fix checkpatch warnings [ALSA] soc - poodle - Fix checkpatch warnings [ALSA] soc - corgi - Fix checkpatch warnings [ALSA] soc - s3c24xx-i2s - Add missing spaces [ALSA] soc - s3c24xx-i2s - Fix tab/space breakage [ALSA] soc - s3c24xx-i2s - Use linux/io.h [ALSA] hda - Fix Thinkpad X300 digital mic pcsp - Don't build pcspkr when snd-pcsp is enabled [ALSA] hda - Fix model for Acer Aspire 5720z [ALSA] soc - s3c24xx - Declare suspend and resume static [ALSA] soc - s3c24xx - Improve diagnostic output [ALSA] Fix possible races at free_irq in PCI drivers ...
This commit is contained in:
Коммит
38ccc197eb
8
CREDITS
8
CREDITS
|
@ -403,6 +403,8 @@ D: Linux CD and Support Giveaway List
|
|||
N: Erik Inge Bolsø
|
||||
E: knan@mo.himolde.no
|
||||
D: Misc kernel hacks
|
||||
D: Updated PC speaker driver for 2.3
|
||||
S: Norway
|
||||
|
||||
N: Andreas E. Bombe
|
||||
E: andreas.bombe@munich.netsurf.de
|
||||
|
@ -3116,6 +3118,12 @@ S: Post Office Box 64132
|
|||
S: Sunnyvale, California 94088-4132
|
||||
S: USA
|
||||
|
||||
N: Stas Sergeev
|
||||
E: stsp@users.sourceforge.net
|
||||
D: PCM PC-Speaker driver
|
||||
D: misc fixes
|
||||
S: Russia
|
||||
|
||||
N: Simon Shapiro
|
||||
E: shimon@i-Connect.Net
|
||||
W: http://www.-i-Connect.Net/~shimon
|
||||
|
|
|
@ -284,6 +284,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
control correctly. If you have problems regarding this, try
|
||||
another ALSA compliant mixer (alsamixer works).
|
||||
|
||||
Module snd-aw2
|
||||
--------------
|
||||
|
||||
Module for Audiowerk2 sound card
|
||||
|
||||
This module supports multiple cards.
|
||||
|
||||
Module snd-azt2320
|
||||
------------------
|
||||
|
||||
|
@ -818,19 +825,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
hippo_1 Hippo (Benq) with jack detection
|
||||
sony-assamd Sony ASSAMD
|
||||
ultra Samsung Q1 Ultra Vista model
|
||||
lenovo-3000 Lenovo 3000 y410
|
||||
basic fixed pin assignment w/o SPDIF
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC268
|
||||
ALC267/268
|
||||
quanta-il1 Quanta IL1 mini-notebook
|
||||
3stack 3-stack model
|
||||
toshiba Toshiba A205
|
||||
acer Acer laptops
|
||||
dell Dell OEM laptops (Vostro 1200)
|
||||
zepto Zepto laptops
|
||||
test for testing/debugging purpose, almost all controls can
|
||||
adjusted. Appearing only when compiled with
|
||||
$CONFIG_SND_DEBUG=y
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC269
|
||||
basic Basic preset
|
||||
|
||||
ALC662
|
||||
3stack-dig 3-stack (2-channel) with SPDIF
|
||||
3stack-6ch 3-stack (6-channel)
|
||||
|
@ -871,10 +884,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
lenovo-nb0763 Lenovo NB0763
|
||||
lenovo-ms7195-dig Lenovo MS7195
|
||||
haier-w66 Haier W66
|
||||
6stack-hp HP machines with 6stack (Nettle boards)
|
||||
3stack-hp HP machines with 3stack (Lucknow, Samba boards)
|
||||
6stack-dell Dell machines with 6stack (Inspiron 530)
|
||||
mitac Mitac 8252D
|
||||
clevo-m720 Clevo M720 laptop series
|
||||
fujitsu-pi2515 Fujitsu AMILO Pi2515
|
||||
auto auto-config reading BIOS (default)
|
||||
|
||||
ALC861/660
|
||||
|
@ -911,6 +925,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
3stack 3-stack mode (default)
|
||||
6stack 6-stack mode
|
||||
|
||||
AD1884A / AD1883 / AD1984A / AD1984B
|
||||
desktop 3-stack desktop (default)
|
||||
laptop laptop with HP jack sensing
|
||||
mobile mobile devices with HP jack sensing
|
||||
thinkpad Lenovo Thinkpad X300
|
||||
|
||||
AD1884
|
||||
N/A
|
||||
|
||||
|
@ -936,7 +956,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
|
||||
ultra 2-channel with EAPD (Samsung Ultra tablet PC)
|
||||
|
||||
AD1988
|
||||
AD1988/AD1988B/AD1989A/AD1989B
|
||||
6stack 6-jack
|
||||
6stack-dig ditto with SPDIF
|
||||
3stack 3-jack
|
||||
|
@ -979,6 +999,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
dell-m26 Dell Inspiron 1501
|
||||
dell-m27 Dell Inspiron E1705/9400
|
||||
gateway Gateway laptops with EAPD control
|
||||
panasonic Panasonic CF-74
|
||||
|
||||
STAC9205/9254
|
||||
ref Reference board
|
||||
|
@ -1017,6 +1038,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
3stack D965 3stack
|
||||
5stack D965 5stack + SPDIF
|
||||
dell-3stack Dell Dimension E520
|
||||
dell-bios Fixes with Dell BIOS setup
|
||||
|
||||
STAC92HD71B*
|
||||
ref Reference board
|
||||
dell-m4-1 Dell desktops
|
||||
dell-m4-2 Dell desktops
|
||||
|
||||
STAC92HD73*
|
||||
ref Reference board
|
||||
dell-m6 Dell desktops
|
||||
|
||||
STAC9872
|
||||
vaio Setup for VAIO FE550G/SZ110
|
||||
|
@ -1590,6 +1621,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
Power management is _not_ supported.
|
||||
|
||||
Module snd-pcsp
|
||||
-----------------
|
||||
|
||||
Module for internal PC-Speaker.
|
||||
|
||||
nforce_wa - enable NForce chipset workaround. Expect bad sound.
|
||||
|
||||
This module supports system beeps, some kind of PCM playback and
|
||||
even a few mixer controls.
|
||||
|
||||
Module snd-pcxhr
|
||||
----------------
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ if INPUT_MISC
|
|||
config INPUT_PCSPKR
|
||||
tristate "PC Speaker support"
|
||||
depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES
|
||||
depends on SND_PCSP=n
|
||||
help
|
||||
Say Y here if you want the standard PC Speaker to be used for
|
||||
bells and whistles.
|
||||
|
|
|
@ -690,10 +690,8 @@ MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
|
|||
static int snd_cx88_free(snd_cx88_card_t *chip)
|
||||
{
|
||||
|
||||
if (chip->irq >= 0){
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
}
|
||||
|
||||
cx88_core_put(chip->core,chip->pci);
|
||||
|
||||
|
|
|
@ -954,10 +954,8 @@ static void snd_saa7134_free(struct snd_card * card)
|
|||
if (chip->dev->dmasound.priv_data == NULL)
|
||||
return;
|
||||
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, &chip->dev->dmasound);
|
||||
}
|
||||
|
||||
chip->dev->dmasound.priv_data = NULL;
|
||||
|
||||
|
|
|
@ -397,6 +397,7 @@
|
|||
#define AC97_HAS_NO_TONE (1<<16) /* no Tone volume */
|
||||
#define AC97_HAS_NO_STD_PCM (1<<17) /* no standard AC97 PCM volume and mute */
|
||||
#define AC97_HAS_NO_AUX (1<<18) /* no standard AC97 AUX volume and mute */
|
||||
#define AC97_HAS_8CH (1<<19) /* supports 8-channel output */
|
||||
|
||||
/* rates indexes */
|
||||
#define AC97_RATES_FRONT_DAC 0
|
||||
|
|
|
@ -182,6 +182,7 @@ struct ak4114 {
|
|||
unsigned char rcs0;
|
||||
unsigned char rcs1;
|
||||
struct delayed_work work;
|
||||
unsigned int check_flags;
|
||||
void *change_callback_private;
|
||||
void (*change_callback)(struct ak4114 *ak4114, unsigned char c0, unsigned char c1);
|
||||
};
|
||||
|
|
|
@ -68,7 +68,7 @@ struct snd_akm4xxx {
|
|||
enum {
|
||||
SND_AK4524, SND_AK4528, SND_AK4529,
|
||||
SND_AK4355, SND_AK4358, SND_AK4381,
|
||||
SND_AK5365, NON_AKM
|
||||
SND_AK5365
|
||||
} type;
|
||||
|
||||
/* (array) information of combined codecs */
|
||||
|
|
|
@ -112,6 +112,14 @@
|
|||
#define IEC958_AES3_CON_CLOCK_1000PPM (0<<4) /* 1000 ppm */
|
||||
#define IEC958_AES3_CON_CLOCK_50PPM (1<<4) /* 50 ppm */
|
||||
#define IEC958_AES3_CON_CLOCK_VARIABLE (2<<4) /* variable pitch */
|
||||
#define IEC958_AES4_CON_MAX_WORDLEN_24 (1<<0) /* 0 = 20-bit, 1 = 24-bit */
|
||||
#define IEC958_AES4_CON_WORDLEN (7<<1) /* mask - sample word length */
|
||||
#define IEC958_AES4_CON_WORDLEN_NOTID (0<<1) /* not indicated */
|
||||
#define IEC958_AES4_CON_WORDLEN_20_16 (1<<1) /* 20-bit or 16-bit */
|
||||
#define IEC958_AES4_CON_WORDLEN_22_18 (2<<1) /* 22-bit or 18-bit */
|
||||
#define IEC958_AES4_CON_WORDLEN_23_19 (4<<1) /* 23-bit or 19-bit */
|
||||
#define IEC958_AES4_CON_WORDLEN_24_20 (5<<1) /* 24-bit or 20-bit */
|
||||
#define IEC958_AES4_CON_WORDLEN_21_17 (6<<1) /* 21-bit or 17-bit */
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
|
|
|
@ -169,4 +169,11 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
|
|||
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
|
||||
/*
|
||||
* virtual master control
|
||||
*/
|
||||
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
||||
const unsigned int *tlv);
|
||||
int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
|
||||
|
||||
#endif /* __SOUND_CONTROL_H */
|
||||
|
|
|
@ -277,8 +277,8 @@ int snd_minor_info_done(void);
|
|||
int snd_minor_info_oss_init(void);
|
||||
int snd_minor_info_oss_done(void);
|
||||
#else
|
||||
#define snd_minor_info_oss_init() /*NOP*/
|
||||
#define snd_minor_info_oss_done() /*NOP*/
|
||||
static inline int snd_minor_info_oss_init(void) { return 0; }
|
||||
static inline int snd_minor_info_oss_done(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/* memory.c */
|
||||
|
@ -310,7 +310,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file);
|
|||
int snd_card_file_remove(struct snd_card *card, struct file *file);
|
||||
|
||||
#ifndef snd_card_set_dev
|
||||
#define snd_card_set_dev(card,devptr) ((card)->dev = (devptr))
|
||||
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
|
||||
#endif
|
||||
|
||||
/* device.c */
|
||||
|
@ -373,7 +373,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
|||
* snd_printd - debug printk
|
||||
* @fmt: format string
|
||||
*
|
||||
* Compiled only when Works like snd_printk() for debugging purpose.
|
||||
* Works like snd_printk() for debugging purposes.
|
||||
* Ignored when CONFIG_SND_DEBUG is not set.
|
||||
*/
|
||||
#define snd_printd(fmt, args...) \
|
||||
|
@ -417,7 +417,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
|||
* snd_printdd - debug printk
|
||||
* @format: format string
|
||||
*
|
||||
* Compiled only when Works like snd_printk() for debugging purpose.
|
||||
* Works like snd_printk() for debugging purposes.
|
||||
* Ignored when CONFIG_SND_DEBUG_DETECT is not set.
|
||||
*/
|
||||
#define snd_printdd(format, args...) snd_printk(format, ##args)
|
||||
|
|
|
@ -102,6 +102,21 @@ struct snd_mpu401 {
|
|||
#define MPU401C(mpu) (mpu)->cport
|
||||
#define MPU401D(mpu) (mpu)->port
|
||||
|
||||
/*
|
||||
* control register bits
|
||||
*/
|
||||
/* read MPU401C() */
|
||||
#define MPU401_RX_EMPTY 0x80
|
||||
#define MPU401_TX_FULL 0x40
|
||||
|
||||
/* write MPU401C() */
|
||||
#define MPU401_RESET 0xff
|
||||
#define MPU401_ENTER_UART 0x3f
|
||||
|
||||
/* read MPU401D() */
|
||||
#define MPU401_ACK 0xfe
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
/* include/version.h. Generated by alsa/ksync script. */
|
||||
#define CONFIG_SND_VERSION "1.0.16rc2"
|
||||
#define CONFIG_SND_DATE " (Thu Jan 31 16:40:16 2008 UTC)"
|
||||
#define CONFIG_SND_VERSION "1.0.16"
|
||||
#define CONFIG_SND_DATE ""
|
||||
|
|
|
@ -72,7 +72,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg
|
|||
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
|
||||
!((GSR | gsr_bits) & GSR_SDONE)) {
|
||||
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
|
||||
__FUNCTION__, reg, GSR | gsr_bits);
|
||||
__func__, reg, GSR | gsr_bits);
|
||||
val = -1;
|
||||
goto out;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne
|
|||
if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
|
||||
!((GSR | gsr_bits) & GSR_CDONE))
|
||||
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
|
||||
__FUNCTION__, reg, GSR | gsr_bits);
|
||||
__func__, reg, GSR | gsr_bits);
|
||||
|
||||
mutex_unlock(&car_mutex);
|
||||
}
|
||||
|
@ -112,6 +112,16 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne
|
|||
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
/* First, try cold reset */
|
||||
#ifdef CONFIG_PXA3xx
|
||||
int timeout;
|
||||
|
||||
/* Hold CLKBPB for 100us */
|
||||
GCR = 0;
|
||||
GCR = GCR_CLKBPB;
|
||||
udelay(100);
|
||||
GCR = 0;
|
||||
#endif
|
||||
|
||||
GCR &= GCR_COLD_RST; /* clear everything but nCRST */
|
||||
GCR &= ~GCR_COLD_RST; /* then assert nCRST */
|
||||
|
||||
|
@ -123,6 +133,14 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
|
|||
clk_disable(ac97conf_clk);
|
||||
GCR = GCR_COLD_RST;
|
||||
udelay(50);
|
||||
#elif defined(CONFIG_PXA3xx)
|
||||
timeout = 1000;
|
||||
/* Can't use interrupts on PXA3xx */
|
||||
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
|
||||
|
||||
GCR = GCR_WARM_RST | GCR_COLD_RST;
|
||||
while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
|
||||
mdelay(10);
|
||||
#else
|
||||
GCR = GCR_COLD_RST;
|
||||
GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
|
||||
|
@ -131,7 +149,7 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
|
|||
|
||||
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
|
||||
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
|
||||
__FUNCTION__, gsr_bits);
|
||||
__func__, gsr_bits);
|
||||
|
||||
/* let's try warm reset */
|
||||
gsr_bits = 0;
|
||||
|
@ -143,6 +161,12 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
|
|||
GCR |= GCR_WARM_RST;
|
||||
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
|
||||
udelay(500);
|
||||
#elif defined(CONFIG_PXA3xx)
|
||||
timeout = 100;
|
||||
/* Can't use interrupts */
|
||||
GCR |= GCR_WARM_RST;
|
||||
while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
|
||||
mdelay(1);
|
||||
#else
|
||||
GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
|
||||
wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
|
||||
|
@ -150,7 +174,7 @@ static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
|
|||
|
||||
if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
|
||||
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
|
||||
__FUNCTION__, gsr_bits);
|
||||
__func__, gsr_bits);
|
||||
}
|
||||
|
||||
GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
|
||||
|
@ -424,6 +448,7 @@ static struct platform_driver pxa2xx_ac97_driver = {
|
|||
.resume = pxa2xx_ac97_resume,
|
||||
.driver = {
|
||||
.name = "pxa2xx-ac97",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -443,3 +468,4 @@ module_exit(pxa2xx_ac97_exit);
|
|||
MODULE_AUTHOR("Nicolas Pitre");
|
||||
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa2xx-ac97");
|
||||
|
|
|
@ -181,3 +181,7 @@ config SND_PCM_XRUN_DEBUG
|
|||
It is usually not required, but if you have trouble with
|
||||
sound clicking when system is loaded, it may help to determine
|
||||
the process or driver which causes the scheduling gaps.
|
||||
|
||||
config SND_VMASTER
|
||||
bool
|
||||
depends on SND
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
|
||||
snd-$(CONFIG_ISA_DMA_API) += isadma.o
|
||||
snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
|
||||
snd-$(CONFIG_SND_VMASTER) += vmaster.o
|
||||
|
||||
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
|
||||
pcm_memory.o
|
||||
|
|
|
@ -254,7 +254,7 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
|
|||
if (likely(df))
|
||||
return df->disconnected_f_op->release(inode, file);
|
||||
|
||||
panic("%s(%p, %p) failed!", __FUNCTION__, inode, file);
|
||||
panic("%s(%p, %p) failed!", __func__, inode, file);
|
||||
}
|
||||
|
||||
static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
|
||||
|
@ -311,6 +311,9 @@ int snd_card_disconnect(struct snd_card *card)
|
|||
struct file *file;
|
||||
int err;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&card->files_lock);
|
||||
if (card->shutdown) {
|
||||
spin_unlock(&card->files_lock);
|
||||
|
@ -322,6 +325,7 @@ int snd_card_disconnect(struct snd_card *card)
|
|||
/* phase 1: disable fops (user space) operations for ALSA API */
|
||||
mutex_lock(&snd_card_mutex);
|
||||
snd_cards[card->number] = NULL;
|
||||
snd_cards_lock &= ~(1 << card->number);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
|
||||
/* phase 2: replace file->f_op with special dummy operations */
|
||||
|
@ -360,6 +364,15 @@ int snd_card_disconnect(struct snd_card *card)
|
|||
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
|
||||
|
||||
snd_info_card_disconnect(card);
|
||||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
if (card->card_dev) {
|
||||
device_unregister(card->card_dev);
|
||||
card->card_dev = NULL;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_PM
|
||||
wake_up(&card->power_sleep);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -401,33 +414,14 @@ static int snd_card_do_free(struct snd_card *card)
|
|||
snd_printk(KERN_WARNING "unable to free card info\n");
|
||||
/* Not fatal error */
|
||||
}
|
||||
#ifndef CONFIG_SYSFS_DEPRECATED
|
||||
if (card->card_dev)
|
||||
device_unregister(card->card_dev);
|
||||
#endif
|
||||
kfree(card);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_card_free_prepare(struct snd_card *card)
|
||||
{
|
||||
if (card == NULL)
|
||||
return -EINVAL;
|
||||
(void) snd_card_disconnect(card);
|
||||
mutex_lock(&snd_card_mutex);
|
||||
snd_cards[card->number] = NULL;
|
||||
snd_cards_lock &= ~(1 << card->number);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
#ifdef CONFIG_PM
|
||||
wake_up(&card->power_sleep);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_card_free_when_closed(struct snd_card *card)
|
||||
{
|
||||
int free_now = 0;
|
||||
int ret = snd_card_free_prepare(card);
|
||||
int ret = snd_card_disconnect(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -447,7 +441,7 @@ EXPORT_SYMBOL(snd_card_free_when_closed);
|
|||
|
||||
int snd_card_free(struct snd_card *card)
|
||||
{
|
||||
int ret = snd_card_free_prepare(card);
|
||||
int ret = snd_card_disconnect(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ void snd_verbose_printk(const char *file, int line, const char *format, ...)
|
|||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
|
@ -60,7 +60,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
|
|||
{
|
||||
va_list args;
|
||||
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '9' && format[2] == '>') {
|
||||
if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
|
||||
char tmp[] = "<0>";
|
||||
tmp[1] = format[1];
|
||||
printk("%sALSA %s:%d: ", tmp, file, line);
|
||||
|
|
|
@ -1257,6 +1257,8 @@ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
|
|||
{ SOUND_MIXER_DIGITAL3, "Digital", 2 },
|
||||
{ SOUND_MIXER_PHONEIN, "Phone", 0 },
|
||||
{ SOUND_MIXER_PHONEOUT, "Master Mono", 0 },
|
||||
{ SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/
|
||||
{ SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/
|
||||
{ SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */
|
||||
{ SOUND_MIXER_VIDEO, "Video", 0 },
|
||||
{ SOUND_MIXER_RADIO, "Radio", 0 },
|
||||
|
|
|
@ -245,8 +245,13 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
|
|||
info->nr_voices = rec->nr_voices;
|
||||
if (info->nr_voices > 0) {
|
||||
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
|
||||
if (!info->ch)
|
||||
BUG();
|
||||
if (!info->ch) {
|
||||
snd_printk(KERN_ERR "Cannot malloc\n");
|
||||
rec->oper.close(&info->arg);
|
||||
module_put(rec->oper.owner);
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
continue;
|
||||
}
|
||||
reset_channels(info);
|
||||
}
|
||||
debug_printk(("synth %d assigned\n", i));
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
/*
|
||||
* a subset of information returned via ctl info callback
|
||||
|
@ -34,6 +35,7 @@ struct link_master {
|
|||
struct list_head slaves;
|
||||
struct link_ctl_info info;
|
||||
int val; /* the master value */
|
||||
unsigned int tlv[4];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -253,6 +255,8 @@ int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
|
|||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_ctl_add_slave);
|
||||
|
||||
/*
|
||||
* ctl callbacks for master controls
|
||||
*/
|
||||
|
@ -355,10 +359,13 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
|||
kctl->private_free = master_free;
|
||||
|
||||
/* additional (constant) TLV read */
|
||||
if (tlv) {
|
||||
/* FIXME: this assumes that the max volume is 0 dB */
|
||||
if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
|
||||
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
|
||||
kctl->tlv.p = tlv;
|
||||
memcpy(master->tlv, tlv, sizeof(master->tlv));
|
||||
kctl->tlv.p = master->tlv;
|
||||
}
|
||||
|
||||
return kctl;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_ctl_make_virtual_master);
|
|
@ -4,6 +4,24 @@ menu "Generic devices"
|
|||
depends on SND!=n
|
||||
|
||||
|
||||
config SND_PCSP
|
||||
tristate "Internal PC speaker support"
|
||||
depends on X86_PC && HIGH_RES_TIMERS
|
||||
depends on INPUT
|
||||
help
|
||||
If you don't have a sound card in your computer, you can include a
|
||||
driver for the PC speaker which allows it to act like a primitive
|
||||
sound card.
|
||||
This driver also replaces the pcspkr driver for beeps.
|
||||
|
||||
You can compile this as a module which will be called snd-pcsp.
|
||||
|
||||
You don't need this driver if you only want your pc-speaker to beep.
|
||||
You don't need this driver if you have a tablet piezo beeper
|
||||
in your PC instead of the real speaker.
|
||||
|
||||
It should not hurt to say Y or M here in all other cases.
|
||||
|
||||
config SND_MPU401_UART
|
||||
tristate
|
||||
select SND_RAWMIDI
|
||||
|
|
|
@ -20,4 +20,4 @@ obj-$(CONFIG_SND_MTS64) += snd-mts64.o
|
|||
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
|
||||
obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
|
||||
|
||||
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
|
||||
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
|
||||
|
|
|
@ -181,10 +181,10 @@ struct snd_dummy_pcm {
|
|||
struct snd_dummy *dummy;
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
unsigned int pcm_size;
|
||||
unsigned int pcm_count;
|
||||
unsigned int pcm_buffer_size;
|
||||
unsigned int pcm_period_size;
|
||||
unsigned int pcm_bps; /* bytes per second */
|
||||
unsigned int pcm_jiffie; /* bytes per one jiffie */
|
||||
unsigned int pcm_hz; /* HZ */
|
||||
unsigned int pcm_irq_pos; /* IRQ position */
|
||||
unsigned int pcm_buf_pos; /* position in buffer */
|
||||
struct snd_pcm_substream *substream;
|
||||
|
@ -230,19 +230,24 @@ static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm = runtime->private_data;
|
||||
unsigned int bps;
|
||||
int bps;
|
||||
|
||||
bps = snd_pcm_format_width(runtime->format) * runtime->rate *
|
||||
runtime->channels / 8;
|
||||
|
||||
bps = runtime->rate * runtime->channels;
|
||||
bps *= snd_pcm_format_width(runtime->format);
|
||||
bps /= 8;
|
||||
if (bps <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
dpcm->pcm_bps = bps;
|
||||
dpcm->pcm_jiffie = bps / HZ;
|
||||
dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
dpcm->pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
dpcm->pcm_hz = HZ;
|
||||
dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
|
||||
dpcm->pcm_irq_pos = 0;
|
||||
dpcm->pcm_buf_pos = 0;
|
||||
|
||||
snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
|
||||
bytes_to_samples(runtime, runtime->dma_bytes));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -254,11 +259,11 @@ static void snd_card_dummy_pcm_timer_function(unsigned long data)
|
|||
spin_lock_irqsave(&dpcm->lock, flags);
|
||||
dpcm->timer.expires = 1 + jiffies;
|
||||
add_timer(&dpcm->timer);
|
||||
dpcm->pcm_irq_pos += dpcm->pcm_jiffie;
|
||||
dpcm->pcm_buf_pos += dpcm->pcm_jiffie;
|
||||
dpcm->pcm_buf_pos %= dpcm->pcm_size;
|
||||
if (dpcm->pcm_irq_pos >= dpcm->pcm_count) {
|
||||
dpcm->pcm_irq_pos %= dpcm->pcm_count;
|
||||
dpcm->pcm_irq_pos += dpcm->pcm_bps;
|
||||
dpcm->pcm_buf_pos += dpcm->pcm_bps;
|
||||
dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
|
||||
if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
|
||||
dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
|
||||
spin_unlock_irqrestore(&dpcm->lock, flags);
|
||||
snd_pcm_period_elapsed(dpcm->substream);
|
||||
} else
|
||||
|
@ -270,7 +275,7 @@ static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *su
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dummy_pcm *dpcm = runtime->private_data;
|
||||
|
||||
return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
|
||||
return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_card_dummy_playback =
|
||||
|
|
|
@ -1191,8 +1191,6 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
|
|||
return err;
|
||||
}
|
||||
|
||||
snd_card_set_dev(card, &pfdev->dev);
|
||||
|
||||
*rml403_ac97cr = ml403_ac97cr;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1330,11 +1328,15 @@ static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER);
|
||||
|
||||
static struct platform_driver snd_ml403_ac97cr_driver = {
|
||||
.probe = snd_ml403_ac97cr_probe,
|
||||
.remove = snd_ml403_ac97cr_remove,
|
||||
.driver = {
|
||||
.name = SND_ML403_AC97CR_DRIVER,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -49,12 +49,10 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu);
|
|||
|
||||
*/
|
||||
|
||||
#define snd_mpu401_input_avail(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x80))
|
||||
#define snd_mpu401_output_ready(mpu) (!(mpu->read(mpu, MPU401C(mpu)) & 0x40))
|
||||
|
||||
#define MPU401_RESET 0xff
|
||||
#define MPU401_ENTER_UART 0x3f
|
||||
#define MPU401_ACK 0xfe
|
||||
#define snd_mpu401_input_avail(mpu) \
|
||||
(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_RX_EMPTY))
|
||||
#define snd_mpu401_output_ready(mpu) \
|
||||
(!(mpu->read(mpu, MPU401C(mpu)) & MPU401_TX_FULL))
|
||||
|
||||
/* Build in lowlevel io */
|
||||
static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data,
|
||||
|
@ -425,16 +423,17 @@ static void snd_mpu401_uart_input_read(struct snd_mpu401 * mpu)
|
|||
static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
|
||||
{
|
||||
unsigned char byte;
|
||||
int max = 256, timeout;
|
||||
int max = 256;
|
||||
|
||||
do {
|
||||
if (snd_rawmidi_transmit_peek(mpu->substream_output,
|
||||
&byte, 1) == 1) {
|
||||
for (timeout = 100; timeout > 0; timeout--) {
|
||||
if (snd_mpu401_output_ready(mpu))
|
||||
break;
|
||||
}
|
||||
if (timeout == 0)
|
||||
/*
|
||||
* Try twice because there is hardware that insists on
|
||||
* setting the output busy bit after each write.
|
||||
*/
|
||||
if (!snd_mpu401_output_ready(mpu) &&
|
||||
!snd_mpu401_output_ready(mpu))
|
||||
break; /* Tx FIFO full - try again later */
|
||||
mpu->write(mpu, byte, MPU401D(mpu));
|
||||
snd_rawmidi_transmit_ack(mpu->substream_output, 1);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
|
||||
obj-$(CONFIG_SND_PCSP) += snd-pcsp.o
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* PC-Speaker driver for Linux
|
||||
*
|
||||
* Copyright (C) 1997-2001 David Woodhouse
|
||||
* Copyright (C) 2001-2008 Stas Sergeev
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/bitops.h>
|
||||
#include "pcsp_input.h"
|
||||
#include "pcsp.h"
|
||||
|
||||
MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
|
||||
MODULE_DESCRIPTION("PC-Speaker driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}");
|
||||
MODULE_ALIAS("platform:pcspkr");
|
||||
|
||||
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
|
||||
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
|
||||
static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
|
||||
|
||||
module_param(index, int, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
|
||||
module_param(id, charp, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for pcsp soundcard.");
|
||||
module_param(enable, bool, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable PC-Speaker sound.");
|
||||
|
||||
struct snd_pcsp pcsp_chip;
|
||||
|
||||
static int __devinit snd_pcsp_create(struct snd_card *card)
|
||||
{
|
||||
static struct snd_device_ops ops = { };
|
||||
struct timespec tp;
|
||||
int err;
|
||||
int div, min_div, order;
|
||||
|
||||
hrtimer_get_res(CLOCK_MONOTONIC, &tp);
|
||||
if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
|
||||
printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
|
||||
"(%linS)\n", tp.tv_nsec);
|
||||
printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
|
||||
"enabled.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
|
||||
min_div = MIN_DIV;
|
||||
else
|
||||
min_div = MAX_DIV;
|
||||
#if PCSP_DEBUG
|
||||
printk("PCSP: lpj=%li, min_div=%i, res=%li\n",
|
||||
loops_per_jiffy, min_div, tp.tv_nsec);
|
||||
#endif
|
||||
|
||||
div = MAX_DIV / min_div;
|
||||
order = fls(div) - 1;
|
||||
|
||||
pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE);
|
||||
pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE);
|
||||
pcsp_chip.playback_ptr = 0;
|
||||
pcsp_chip.period_ptr = 0;
|
||||
atomic_set(&pcsp_chip.timer_active, 0);
|
||||
pcsp_chip.enable = 1;
|
||||
pcsp_chip.pcspkr = 1;
|
||||
|
||||
spin_lock_init(&pcsp_chip.substream_lock);
|
||||
|
||||
pcsp_chip.card = card;
|
||||
pcsp_chip.port = 0x61;
|
||||
pcsp_chip.irq = -1;
|
||||
pcsp_chip.dma = -1;
|
||||
|
||||
/* Register device */
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int err;
|
||||
|
||||
if (devnum != 0)
|
||||
return -EINVAL;
|
||||
|
||||
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE;
|
||||
pcsp_chip.timer.function = pcsp_do_timer;
|
||||
|
||||
card = snd_card_new(index, id, THIS_MODULE, 0);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
|
||||
err = snd_pcsp_create(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcsp_new_pcm(&pcsp_chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcsp_new_mixer(&pcsp_chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
snd_card_set_dev(pcsp_chip.card, dev);
|
||||
|
||||
strcpy(card->driver, "PC-Speaker");
|
||||
strcpy(card->shortname, "pcsp");
|
||||
sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
|
||||
pcsp_chip.port);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit alsa_card_pcsp_init(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_card_pcsp_probe(0, dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "PC-Speaker initialization failed.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_PAGEALLOC
|
||||
/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
|
||||
printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
|
||||
"which may make the sound noisy.\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip)
|
||||
{
|
||||
snd_card_free(chip->card);
|
||||
}
|
||||
|
||||
static int __devinit pcsp_probe(struct platform_device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = alsa_card_pcsp_init(&dev->dev);
|
||||
if (err < 0) {
|
||||
pcspkr_input_remove(pcsp_chip.input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, &pcsp_chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit pcsp_remove(struct platform_device *dev)
|
||||
{
|
||||
struct snd_pcsp *chip = platform_get_drvdata(dev);
|
||||
alsa_card_pcsp_exit(chip);
|
||||
pcspkr_input_remove(chip->input_dev);
|
||||
platform_set_drvdata(dev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcsp_stop_beep(struct snd_pcsp *chip)
|
||||
{
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
if (!chip->playback_substream)
|
||||
pcspkr_stop_sound();
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
}
|
||||
|
||||
static int pcsp_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct snd_pcsp *chip = platform_get_drvdata(dev);
|
||||
pcsp_stop_beep(chip);
|
||||
snd_pcm_suspend_all(chip->pcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcsp_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct snd_pcsp *chip = platform_get_drvdata(dev);
|
||||
pcsp_stop_beep(chip);
|
||||
}
|
||||
|
||||
static struct platform_driver pcsp_platform_driver = {
|
||||
.driver = {
|
||||
.name = "pcspkr",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = pcsp_probe,
|
||||
.remove = __devexit_p(pcsp_remove),
|
||||
.suspend = pcsp_suspend,
|
||||
.shutdown = pcsp_shutdown,
|
||||
};
|
||||
|
||||
static int __init pcsp_init(void)
|
||||
{
|
||||
if (!enable)
|
||||
return -ENODEV;
|
||||
return platform_driver_register(&pcsp_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit pcsp_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pcsp_platform_driver);
|
||||
}
|
||||
|
||||
module_init(pcsp_init);
|
||||
module_exit(pcsp_exit);
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* PC-Speaker driver for Linux
|
||||
*
|
||||
* Copyright (C) 1993-1997 Michael Beck
|
||||
* Copyright (C) 1997-2001 David Woodhouse
|
||||
* Copyright (C) 2001-2008 Stas Sergeev
|
||||
*/
|
||||
|
||||
#ifndef __PCSP_H__
|
||||
#define __PCSP_H__
|
||||
|
||||
#include <linux/hrtimer.h>
|
||||
#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
|
||||
/* Use the global PIT lock ! */
|
||||
#include <asm/i8253.h>
|
||||
#else
|
||||
#include <asm/8253pit.h>
|
||||
static DEFINE_SPINLOCK(i8253_lock);
|
||||
#endif
|
||||
|
||||
#define PCSP_SOUND_VERSION 0x400 /* read 4.00 */
|
||||
#define PCSP_DEBUG 0
|
||||
|
||||
/* default timer freq for PC-Speaker: 18643 Hz */
|
||||
#define DIV_18KHZ 64
|
||||
#define MAX_DIV DIV_18KHZ
|
||||
#define CUR_DIV() (MAX_DIV >> chip->treble)
|
||||
#define PCSP_MAX_TREBLE 1
|
||||
|
||||
/* unfortunately, with hrtimers 37KHz does not work very well :( */
|
||||
#define PCSP_DEFAULT_TREBLE 0
|
||||
#define MIN_DIV (MAX_DIV >> PCSP_MAX_TREBLE)
|
||||
|
||||
/* wild guess */
|
||||
#define PCSP_MIN_LPJ 1000000
|
||||
#define PCSP_DEFAULT_SDIV (DIV_18KHZ >> 1)
|
||||
#define PCSP_DEFAULT_SRATE (PIT_TICK_RATE / PCSP_DEFAULT_SDIV)
|
||||
#define PCSP_INDEX_INC() (1 << (PCSP_MAX_TREBLE - chip->treble))
|
||||
#define PCSP_RATE() (PIT_TICK_RATE / CUR_DIV())
|
||||
#define PCSP_MIN_RATE__1 MAX_DIV/PIT_TICK_RATE
|
||||
#define PCSP_MAX_RATE__1 MIN_DIV/PIT_TICK_RATE
|
||||
#define PCSP_MAX_PERIOD_NS (1000000000ULL * PCSP_MIN_RATE__1)
|
||||
#define PCSP_MIN_PERIOD_NS (1000000000ULL * PCSP_MAX_RATE__1)
|
||||
#define PCSP_CALC_NS(div) ({ \
|
||||
u64 __val = 1000000000ULL * (div); \
|
||||
do_div(__val, PIT_TICK_RATE); \
|
||||
__val; \
|
||||
})
|
||||
#define PCSP_PERIOD_NS() PCSP_CALC_NS(CUR_DIV())
|
||||
|
||||
#define PCSP_MAX_PERIOD_SIZE (64*1024)
|
||||
#define PCSP_MAX_PERIODS 512
|
||||
#define PCSP_BUFFER_SIZE (128*1024)
|
||||
|
||||
struct snd_pcsp {
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct input_dev *input_dev;
|
||||
struct hrtimer timer;
|
||||
unsigned short port, irq, dma;
|
||||
spinlock_t substream_lock;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
size_t playback_ptr;
|
||||
size_t period_ptr;
|
||||
atomic_t timer_active;
|
||||
int thalf;
|
||||
u64 ns_rem;
|
||||
unsigned char val61;
|
||||
int enable;
|
||||
int max_treble;
|
||||
int treble;
|
||||
int pcspkr;
|
||||
};
|
||||
|
||||
extern struct snd_pcsp pcsp_chip;
|
||||
|
||||
extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
|
||||
|
||||
extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
|
||||
extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* PC Speaker beeper driver for Linux
|
||||
*
|
||||
* Copyright (c) 2002 Vojtech Pavlik
|
||||
* Copyright (c) 1992 Orest Zborowski
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/io.h>
|
||||
#include "pcsp.h"
|
||||
|
||||
static void pcspkr_do_sound(unsigned int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i8253_lock, flags);
|
||||
|
||||
if (count) {
|
||||
/* enable counter 2 */
|
||||
outb_p(inb_p(0x61) | 3, 0x61);
|
||||
/* set command for counter 2, 2 byte write */
|
||||
outb_p(0xB6, 0x43);
|
||||
/* select desired HZ */
|
||||
outb_p(count & 0xff, 0x42);
|
||||
outb((count >> 8) & 0xff, 0x42);
|
||||
} else {
|
||||
/* disable counter 2 */
|
||||
outb(inb_p(0x61) & 0xFC, 0x61);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&i8253_lock, flags);
|
||||
}
|
||||
|
||||
void pcspkr_stop_sound(void)
|
||||
{
|
||||
pcspkr_do_sound(0);
|
||||
}
|
||||
|
||||
static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
||||
if (atomic_read(&pcsp_chip.timer_active) || !pcsp_chip.pcspkr)
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case EV_SND:
|
||||
switch (code) {
|
||||
case SND_BELL:
|
||||
if (value)
|
||||
value = 1000;
|
||||
case SND_TONE:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value > 20 && value < 32767)
|
||||
count = PIT_TICK_RATE / value;
|
||||
|
||||
pcspkr_do_sound(count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct input_dev *input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev->name = "PC Speaker";
|
||||
input_dev->phys = "isa0061/input0";
|
||||
input_dev->id.bustype = BUS_ISA;
|
||||
input_dev->id.vendor = 0x001f;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->dev.parent = dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_SND);
|
||||
input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
|
||||
input_dev->event = pcspkr_input_event;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err) {
|
||||
input_free_device(input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
*rdev = input_dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcspkr_input_remove(struct input_dev *dev)
|
||||
{
|
||||
pcspkr_stop_sound();
|
||||
input_unregister_device(dev); /* this also does kfree() */
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* PC-Speaker driver for Linux
|
||||
*
|
||||
* Copyright (C) 2001-2008 Stas Sergeev
|
||||
*/
|
||||
|
||||
#ifndef __PCSP_INPUT_H__
|
||||
#define __PCSP_INPUT_H__
|
||||
|
||||
int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev);
|
||||
int pcspkr_input_remove(struct input_dev *dev);
|
||||
void pcspkr_stop_sound(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* PC-Speaker driver for Linux
|
||||
*
|
||||
* Copyright (C) 1993-1997 Michael Beck
|
||||
* Copyright (C) 1997-2001 David Woodhouse
|
||||
* Copyright (C) 2001-2008 Stas Sergeev
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/io.h>
|
||||
#include "pcsp.h"
|
||||
|
||||
static int nforce_wa;
|
||||
module_param(nforce_wa, bool, 0444);
|
||||
MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
|
||||
"(expect bad sound)");
|
||||
|
||||
static void pcsp_start_timer(unsigned long dummy)
|
||||
{
|
||||
hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need the hrtimer_start as a tasklet to avoid
|
||||
* the nasty locking problem. :(
|
||||
* The problem:
|
||||
* - The timer handler is called with the cpu_base->lock
|
||||
* already held by hrtimer code.
|
||||
* - snd_pcm_period_elapsed() takes the
|
||||
* substream->self_group.lock.
|
||||
* So far so good.
|
||||
* But the snd_pcsp_trigger() is called with the
|
||||
* substream->self_group.lock held, and it calls
|
||||
* hrtimer_start(), which takes the cpu_base->lock.
|
||||
* You see the problem. We have the code pathes
|
||||
* which take two locks in a reverse order. This
|
||||
* can deadlock and the lock validator complains.
|
||||
* The only solution I could find was to move the
|
||||
* hrtimer_start() into a tasklet. -stsp
|
||||
*/
|
||||
static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
|
||||
|
||||
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char timer_cnt, val;
|
||||
int periods_elapsed;
|
||||
u64 ns;
|
||||
size_t period_bytes, buffer_bytes;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
|
||||
|
||||
if (chip->thalf) {
|
||||
outb(chip->val61, 0x61);
|
||||
chip->thalf = 0;
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
return HRTIMER_NORESTART;
|
||||
hrtimer_forward(&chip->timer, chip->timer.expires,
|
||||
ktime_set(0, chip->ns_rem));
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
/* hrtimer calls us from both hardirq and softirq contexts,
|
||||
* so irqsave :( */
|
||||
spin_lock_irqsave(&chip->substream_lock, flags);
|
||||
/* Takashi Iwai says regarding this extra lock:
|
||||
|
||||
If the irq handler handles some data on the DMA buffer, it should
|
||||
do snd_pcm_stream_lock().
|
||||
That protects basically against all races among PCM callbacks, yes.
|
||||
However, there are two remaining issues:
|
||||
1. The substream pointer you try to lock isn't protected _before_
|
||||
this lock yet.
|
||||
2. snd_pcm_period_elapsed() itself acquires the lock.
|
||||
The requirement of another lock is because of 1. When you get
|
||||
chip->playback_substream, it's not protected.
|
||||
Keeping this lock while snd_pcm_period_elapsed() assures the substream
|
||||
is still protected (at least, not released). And the other status is
|
||||
handled properly inside snd_pcm_stream_lock() in
|
||||
snd_pcm_period_elapsed().
|
||||
|
||||
*/
|
||||
if (!chip->playback_substream)
|
||||
goto exit_nr_unlock1;
|
||||
substream = chip->playback_substream;
|
||||
snd_pcm_stream_lock(substream);
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
goto exit_nr_unlock2;
|
||||
|
||||
runtime = substream->runtime;
|
||||
/* assume it is u8 mono */
|
||||
val = runtime->dma_area[chip->playback_ptr];
|
||||
timer_cnt = val * CUR_DIV() / 256;
|
||||
|
||||
if (timer_cnt && chip->enable) {
|
||||
spin_lock(&i8253_lock);
|
||||
if (!nforce_wa) {
|
||||
outb_p(chip->val61, 0x61);
|
||||
outb_p(timer_cnt, 0x42);
|
||||
outb(chip->val61 ^ 1, 0x61);
|
||||
} else {
|
||||
outb(chip->val61 ^ 2, 0x61);
|
||||
chip->thalf = 1;
|
||||
}
|
||||
spin_unlock(&i8253_lock);
|
||||
}
|
||||
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
|
||||
chip->playback_ptr += PCSP_INDEX_INC();
|
||||
periods_elapsed = chip->playback_ptr - chip->period_ptr;
|
||||
if (periods_elapsed < 0) {
|
||||
printk(KERN_WARNING "PCSP: playback_ptr inconsistent "
|
||||
"(%zi %zi %zi)\n",
|
||||
chip->playback_ptr, period_bytes, buffer_bytes);
|
||||
periods_elapsed += buffer_bytes;
|
||||
}
|
||||
periods_elapsed /= period_bytes;
|
||||
/* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
|
||||
* or ALSA will BUG on us. */
|
||||
chip->playback_ptr %= buffer_bytes;
|
||||
|
||||
snd_pcm_stream_unlock(substream);
|
||||
|
||||
if (periods_elapsed) {
|
||||
snd_pcm_period_elapsed(substream);
|
||||
chip->period_ptr += periods_elapsed * period_bytes;
|
||||
chip->period_ptr %= buffer_bytes;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&chip->substream_lock, flags);
|
||||
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
chip->ns_rem = PCSP_PERIOD_NS();
|
||||
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
|
||||
chip->ns_rem -= ns;
|
||||
hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns));
|
||||
return HRTIMER_RESTART;
|
||||
|
||||
exit_nr_unlock2:
|
||||
snd_pcm_stream_unlock(substream);
|
||||
exit_nr_unlock1:
|
||||
spin_unlock_irqrestore(&chip->substream_lock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void pcsp_start_playing(struct snd_pcsp *chip)
|
||||
{
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: start_playing called\n");
|
||||
#endif
|
||||
if (atomic_read(&chip->timer_active)) {
|
||||
printk(KERN_ERR "PCSP: Timer already active\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&i8253_lock);
|
||||
chip->val61 = inb(0x61) | 0x03;
|
||||
outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */
|
||||
spin_unlock(&i8253_lock);
|
||||
atomic_set(&chip->timer_active, 1);
|
||||
chip->thalf = 0;
|
||||
|
||||
tasklet_schedule(&pcsp_start_timer_tasklet);
|
||||
}
|
||||
|
||||
static void pcsp_stop_playing(struct snd_pcsp *chip)
|
||||
{
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: stop_playing called\n");
|
||||
#endif
|
||||
if (!atomic_read(&chip->timer_active))
|
||||
return;
|
||||
|
||||
atomic_set(&chip->timer_active, 0);
|
||||
spin_lock(&i8253_lock);
|
||||
/* restore the timer */
|
||||
outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */
|
||||
outb(chip->val61 & 0xFC, 0x61);
|
||||
spin_unlock(&i8253_lock);
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: close called\n");
|
||||
#endif
|
||||
if (atomic_read(&chip->timer_active)) {
|
||||
printk(KERN_ERR "PCSP: timer still active\n");
|
||||
pcsp_stop_playing(chip);
|
||||
}
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
chip->playback_substream = NULL;
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
int err;
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: hw_free called\n");
|
||||
#endif
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: prepare called, "
|
||||
"size=%zi psize=%zi f=%zi f1=%i\n",
|
||||
snd_pcm_lib_buffer_bytes(substream),
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
snd_pcm_lib_buffer_bytes(substream) /
|
||||
snd_pcm_lib_period_bytes(substream),
|
||||
substream->runtime->periods);
|
||||
#endif
|
||||
chip->playback_ptr = 0;
|
||||
chip->period_ptr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: trigger called\n");
|
||||
#endif
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
pcsp_start_playing(chip);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
pcsp_stop_playing(chip);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
|
||||
*substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
return bytes_to_frames(substream->runtime, chip->playback_ptr);
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_pcsp_playback = {
|
||||
.info = (SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_HALF_DUPLEX |
|
||||
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_U8,
|
||||
.rates = SNDRV_PCM_RATE_KNOT,
|
||||
.rate_min = PCSP_DEFAULT_SRATE,
|
||||
.rate_max = PCSP_DEFAULT_SRATE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
.buffer_bytes_max = PCSP_BUFFER_SIZE,
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = PCSP_MAX_PERIOD_SIZE,
|
||||
.periods_min = 2,
|
||||
.periods_max = PCSP_MAX_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: open called\n");
|
||||
#endif
|
||||
if (atomic_read(&chip->timer_active)) {
|
||||
printk(KERN_ERR "PCSP: still active!!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
runtime->hw = snd_pcsp_playback;
|
||||
spin_lock_irq(&chip->substream_lock);
|
||||
chip->playback_substream = substream;
|
||||
spin_unlock_irq(&chip->substream_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops snd_pcsp_playback_ops = {
|
||||
.open = snd_pcsp_playback_open,
|
||||
.close = snd_pcsp_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_pcsp_playback_hw_params,
|
||||
.hw_free = snd_pcsp_playback_hw_free,
|
||||
.prepare = snd_pcsp_playback_prepare,
|
||||
.trigger = snd_pcsp_trigger,
|
||||
.pointer = snd_pcsp_playback_pointer,
|
||||
};
|
||||
|
||||
int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&snd_pcsp_playback_ops);
|
||||
|
||||
chip->pcm->private_data = chip;
|
||||
chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
|
||||
strcpy(chip->pcm->name, "pcsp");
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data
|
||||
(GFP_KERNEL), PCSP_BUFFER_SIZE,
|
||||
PCSP_BUFFER_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* PC-Speaker driver for Linux
|
||||
*
|
||||
* Mixer implementation.
|
||||
* Copyright (C) 2001-2008 Stas Sergeev
|
||||
*/
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include "pcsp.h"
|
||||
|
||||
|
||||
static int pcsp_enable_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_enable_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.integer.value[0] = chip->enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_enable_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
int changed = 0;
|
||||
int enab = ucontrol->value.integer.value[0];
|
||||
if (enab != chip->enable) {
|
||||
chip->enable = enab;
|
||||
changed = 1;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int pcsp_treble_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = chip->max_treble + 1;
|
||||
if (uinfo->value.enumerated.item > chip->max_treble)
|
||||
uinfo->value.enumerated.item = chip->max_treble;
|
||||
sprintf(uinfo->value.enumerated.name, "%d", PCSP_RATE());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_treble_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.enumerated.item[0] = chip->treble;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_treble_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
int changed = 0;
|
||||
int treble = ucontrol->value.enumerated.item[0];
|
||||
if (treble != chip->treble) {
|
||||
chip->treble = treble;
|
||||
#if PCSP_DEBUG
|
||||
printk(KERN_INFO "PCSP: rate set to %i\n", PCSP_RATE());
|
||||
#endif
|
||||
changed = 1;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int pcsp_pcspkr_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_pcspkr_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.integer.value[0] = chip->pcspkr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_pcsp *chip = snd_kcontrol_chip(kcontrol);
|
||||
int changed = 0;
|
||||
int spkr = ucontrol->value.integer.value[0];
|
||||
if (spkr != chip->pcspkr) {
|
||||
chip->pcspkr = spkr;
|
||||
changed = 1;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
#define PCSP_MIXER_CONTROL(ctl_type, ctl_name) \
|
||||
{ \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = ctl_name, \
|
||||
.info = pcsp_##ctl_type##_info, \
|
||||
.get = pcsp_##ctl_type##_get, \
|
||||
.put = pcsp_##ctl_type##_put, \
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new __devinitdata snd_pcsp_controls[] = {
|
||||
PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
|
||||
PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
|
||||
PCSP_MIXER_CONTROL(pcspkr, "PC Speaker Playback Switch"),
|
||||
};
|
||||
|
||||
int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip)
|
||||
{
|
||||
struct snd_card *card = chip->card;
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_pcsp_controls); i++) {
|
||||
err = snd_ctl_add(card,
|
||||
snd_ctl_new1(snd_pcsp_controls + i,
|
||||
chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(card->mixername, "PC-Speaker");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
#include <sound/pcm.h>
|
||||
#include <sound/ak4114.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/info.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei");
|
||||
|
@ -446,6 +447,26 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct ak4114 *ak4114 = entry->private_data;
|
||||
int reg, val;
|
||||
/* all ak4114 registers 0x00 - 0x1f */
|
||||
for (reg = 0; reg < 0x20; reg++) {
|
||||
val = reg_read(ak4114, reg);
|
||||
snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_ak4114_proc_init(struct ak4114 *ak4114)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
if (!snd_card_proc_new(ak4114->card, "ak4114", &entry))
|
||||
snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
|
||||
}
|
||||
|
||||
int snd_ak4114_build(struct ak4114 *ak4114,
|
||||
struct snd_pcm_substream *ply_substream,
|
||||
struct snd_pcm_substream *cap_substream)
|
||||
|
@ -478,6 +499,7 @@ int snd_ak4114_build(struct ak4114 *ak4114,
|
|||
return err;
|
||||
ak4114->kctls[idx] = kctl;
|
||||
}
|
||||
snd_ak4114_proc_init(ak4114);
|
||||
/* trigger workq */
|
||||
schedule_delayed_work(&ak4114->work, HZ / 10);
|
||||
return 0;
|
||||
|
@ -590,7 +612,7 @@ static void ak4114_stats(struct work_struct *work)
|
|||
struct ak4114 *chip = container_of(work, struct ak4114, work.work);
|
||||
|
||||
if (!chip->init)
|
||||
snd_ak4114_check_rate_and_errors(chip, 0);
|
||||
snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
|
||||
|
||||
schedule_delayed_work(&chip->work, HZ / 10);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,8 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state)
|
|||
}
|
||||
|
||||
/* reset procedure for AK4355 and AK4358 */
|
||||
static void ak4355_reset(struct snd_akm4xxx *ak, int state)
|
||||
static void ak435X_reset(struct snd_akm4xxx *ak, int state,
|
||||
unsigned char total_regs)
|
||||
{
|
||||
unsigned char reg;
|
||||
|
||||
|
@ -78,7 +79,7 @@ static void ak4355_reset(struct snd_akm4xxx *ak, int state)
|
|||
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
|
||||
return;
|
||||
}
|
||||
for (reg = 0x00; reg < 0x0b; reg++)
|
||||
for (reg = 0x00; reg < total_regs; reg++)
|
||||
if (reg != 0x01)
|
||||
snd_akm4xxx_write(ak, 0, reg,
|
||||
snd_akm4xxx_get(ak, 0, reg));
|
||||
|
@ -118,8 +119,10 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
|
|||
/* FIXME: needed for ak4529? */
|
||||
break;
|
||||
case SND_AK4355:
|
||||
ak435X_reset(ak, state, 0x0b);
|
||||
break;
|
||||
case SND_AK4358:
|
||||
ak4355_reset(ak, state);
|
||||
ak435X_reset(ak, state, 0x10);
|
||||
break;
|
||||
case SND_AK4381:
|
||||
ak4381_reset(ak, state);
|
||||
|
@ -292,11 +295,6 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
|
|||
case SND_AK5365:
|
||||
/* FIXME: any init sequence? */
|
||||
return;
|
||||
case NON_AKM:
|
||||
/* fake value for non-akm codecs using akm infrastructure
|
||||
* (e.g. of ice1724) - certainly FIXME
|
||||
*/
|
||||
return;
|
||||
default:
|
||||
snd_BUG();
|
||||
return;
|
||||
|
@ -374,6 +372,8 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
|
|||
nval = mask - nval;
|
||||
if (AK_GET_NEEDSMSB(kcontrol->private_value))
|
||||
nval |= 0x80;
|
||||
/* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x,
|
||||
nval %x\n", chip, addr, nval); */
|
||||
snd_akm4xxx_write(ak, chip, addr, nval);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -331,7 +331,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
|
|||
return -EFAULT;
|
||||
if ((file_h.name != RIFF_HEADER) ||
|
||||
(le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
|
||||
snd_printd("%s: Invalid RIFF header\n", __FUNCTION__);
|
||||
snd_printd("%s: Invalid RIFF header\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_ptr += sizeof(file_h);
|
||||
|
@ -340,7 +340,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
|
|||
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
|
||||
return -EFAULT;
|
||||
if (item_type != CSP__HEADER) {
|
||||
snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__);
|
||||
snd_printd("%s: Invalid RIFF file type\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_ptr += sizeof (item_type);
|
||||
|
@ -395,7 +395,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
|
|||
return -EFAULT;
|
||||
|
||||
if (code_h.name != MAIN_HEADER) {
|
||||
snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__);
|
||||
snd_printd("%s: Missing 'main' microcode\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
data_ptr += sizeof(code_h);
|
||||
|
@ -439,7 +439,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
|
|||
p->acc_format = p->acc_width = p->acc_rates = 0;
|
||||
p->mode = 0;
|
||||
snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
|
||||
__FUNCTION__,
|
||||
__func__,
|
||||
le16_to_cpu(funcdesc_h.VOC_type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req);
|
||||
snd_printd("%s: Function #%d not found\n", __func__, info.func_req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -612,7 +612,7 @@ static int get_version(struct snd_sb *chip)
|
|||
static int snd_sb_csp_check_version(struct snd_sb_csp * p)
|
||||
{
|
||||
if (p->version < 0x10 || p->version > 0x1f) {
|
||||
snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version);
|
||||
snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -631,7 +631,7 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
|
|||
spin_lock_irqsave(&p->chip->reg_lock, flags);
|
||||
snd_sbdsp_command(p->chip, 0x01); /* CSP download command */
|
||||
if (snd_sbdsp_get_byte(p->chip)) {
|
||||
snd_printd("%s: Download command failed\n", __FUNCTION__);
|
||||
snd_printd("%s: Download command failed\n", __func__);
|
||||
goto __fail;
|
||||
}
|
||||
/* Send CSP low byte (size - 1) */
|
||||
|
@ -658,7 +658,7 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
|
|||
udelay (10);
|
||||
}
|
||||
if (status != 0x55) {
|
||||
snd_printd("%s: Microcode initialization failed\n", __FUNCTION__);
|
||||
snd_printd("%s: Microcode initialization failed\n", __func__);
|
||||
goto __fail;
|
||||
}
|
||||
} else {
|
||||
|
@ -824,19 +824,19 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel
|
|||
unsigned long flags;
|
||||
|
||||
if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
|
||||
snd_printd("%s: Microcode not loaded\n", __FUNCTION__);
|
||||
snd_printd("%s: Microcode not loaded\n", __func__);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
|
||||
snd_printd("%s: CSP already running\n", __FUNCTION__);
|
||||
snd_printd("%s: CSP already running\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!(sample_width & p->acc_width)) {
|
||||
snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__);
|
||||
snd_printd("%s: Unsupported PCM sample width\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(channels & p->acc_channels)) {
|
||||
snd_printd("%s: Invalid number of channels\n", __FUNCTION__);
|
||||
snd_printd("%s: Invalid number of channels\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -858,11 +858,11 @@ static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channel
|
|||
s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
|
||||
|
||||
if (set_codec_parameter(p->chip, 0x81, s_type)) {
|
||||
snd_printd("%s: Set sample type command failed\n", __FUNCTION__);
|
||||
snd_printd("%s: Set sample type command failed\n", __func__);
|
||||
goto __fail;
|
||||
}
|
||||
if (set_codec_parameter(p->chip, 0x80, 0x00)) {
|
||||
snd_printd("%s: Codec start command failed\n", __FUNCTION__);
|
||||
snd_printd("%s: Codec start command failed\n", __func__);
|
||||
goto __fail;
|
||||
}
|
||||
p->run_width = sample_width;
|
||||
|
|
|
@ -51,7 +51,7 @@ int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
|
|||
outb(val, SBP(chip, COMMAND));
|
||||
return 1;
|
||||
}
|
||||
snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val);
|
||||
snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ int snd_sbdsp_get_byte(struct snd_sb *chip)
|
|||
return val;
|
||||
}
|
||||
}
|
||||
snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port);
|
||||
snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ int snd_sbdsp_reset(struct snd_sb *chip)
|
|||
else
|
||||
break;
|
||||
}
|
||||
snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port);
|
||||
snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
|
@ -795,9 +795,9 @@ static int find_output_space(int dev, char **buf, int *size)
|
|||
#ifdef BE_CONSERVATIVE
|
||||
active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
|
||||
#else
|
||||
active_offs = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT);
|
||||
active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0);
|
||||
/* Check for pointer wrapping situation */
|
||||
if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
|
||||
if (active_offs >= dmap->bytes_in_use)
|
||||
active_offs = 0;
|
||||
active_offs += dmap->byte_counter;
|
||||
#endif
|
||||
|
|
|
@ -3076,8 +3076,7 @@ ali_ac97_get(struct trident_card *card, int secondary, u8 reg)
|
|||
u16 wcontrol;
|
||||
unsigned long flags;
|
||||
|
||||
if (!card)
|
||||
BUG();
|
||||
BUG_ON(!card);
|
||||
|
||||
address = ALI_AC97_READ;
|
||||
if (card->revision == ALI_5451_V02) {
|
||||
|
@ -3148,8 +3147,7 @@ ali_ac97_set(struct trident_card *card, int secondary, u8 reg, u16 val)
|
|||
|
||||
data = ((u32) val) << 16;
|
||||
|
||||
if (!card)
|
||||
BUG();
|
||||
BUG_ON(!card);
|
||||
|
||||
address = ALI_AC97_WRITE;
|
||||
mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
|
||||
|
@ -3213,8 +3211,7 @@ ali_ac97_read(struct ac97_codec *codec, u8 reg)
|
|||
struct trident_card *card = NULL;
|
||||
|
||||
/* Added by Matt Wu */
|
||||
if (!codec)
|
||||
BUG();
|
||||
BUG_ON(!codec);
|
||||
|
||||
card = (struct trident_card *) codec->private_data;
|
||||
|
||||
|
@ -3240,8 +3237,7 @@ ali_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
|
|||
struct trident_card *card;
|
||||
|
||||
/* Added by Matt Wu */
|
||||
if (!codec)
|
||||
BUG();
|
||||
BUG_ON(!codec);
|
||||
|
||||
card = (struct trident_card *) codec->private_data;
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@ enum miscint_bits {
|
|||
#define VALIDATE_MAGIC(FOO,MAG) \
|
||||
({ \
|
||||
if (!(FOO) || (FOO)->magic != MAG) { \
|
||||
printk(invalid_magic,__FUNCTION__); \
|
||||
printk(invalid_magic,__func__); \
|
||||
return -ENXIO; \
|
||||
} \
|
||||
})
|
||||
|
|
|
@ -194,11 +194,11 @@ static void dbgassert(const char *fcn, int line, const char *expr)
|
|||
* DBGRV - debug print function return when verbose
|
||||
*/
|
||||
|
||||
#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__FUNCTION__, __LINE__, #e))
|
||||
#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e))
|
||||
#define DBGDO(x) x
|
||||
#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
|
||||
#define DBGP(fmt, args...) (DBGX("%s: " fmt, __FUNCTION__ , ##args))
|
||||
#define DBGE(fmt, args...) (DBGX("%s" fmt, __FUNCTION__ , ##args))
|
||||
#define DBGP(fmt, args...) (DBGX("%s: " fmt, __func__ , ##args))
|
||||
#define DBGE(fmt, args...) (DBGX("%s" fmt, __func__ , ##args))
|
||||
#define DBGC(rtn) (DBGP("calling %s\n", rtn))
|
||||
#define DBGR() (DBGP("returning\n"))
|
||||
#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
|
||||
|
|
|
@ -122,6 +122,21 @@ config SND_AU8830
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-au8830.
|
||||
|
||||
config SND_AW2
|
||||
tristate "Emagic Audiowerk 2"
|
||||
depends on SND
|
||||
help
|
||||
Say Y here to include support for Emagic Audiowerk 2 soundcards.
|
||||
|
||||
Supported features: Analog and SPDIF output. Analog or SPDIF input.
|
||||
Note: Switch between analog and digital input does not always work.
|
||||
It can produce continuous noise. The workaround is to switch again
|
||||
(and again) between digital and analog input until it works.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-aw2.
|
||||
|
||||
|
||||
config SND_AZT3328
|
||||
tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
|
||||
depends on SND && EXPERIMENTAL
|
||||
|
@ -162,6 +177,7 @@ config SND_CA0106
|
|||
depends on SND
|
||||
select SND_AC97_CODEC
|
||||
select SND_RAWMIDI
|
||||
select SND_VMASTER
|
||||
help
|
||||
Say Y here to include support for the Sound Blaster Audigy LS
|
||||
and Live 24bit.
|
||||
|
@ -517,6 +533,7 @@ config SND_HDA_INTEL
|
|||
tristate "Intel HD Audio"
|
||||
depends on SND
|
||||
select SND_PCM
|
||||
select SND_VMASTER
|
||||
help
|
||||
Say Y here to include support for Intel "High Definition
|
||||
Audio" (Azalia) motherboard devices.
|
||||
|
@ -680,6 +697,7 @@ config SND_ICE1724
|
|||
depends on SND
|
||||
select SND_MPU401_UART
|
||||
select SND_AC97_CODEC
|
||||
select SND_VMASTER
|
||||
help
|
||||
Say Y here to include support for soundcards based on
|
||||
ICE/VT1724/1720 (Envy24HT/PT) chips.
|
||||
|
@ -896,12 +914,12 @@ config SND_VIA82XX_MODEM
|
|||
will be called snd-via82xx-modem.
|
||||
|
||||
config SND_VIRTUOSO
|
||||
tristate "Asus Virtuoso 200 (Xonar)"
|
||||
tristate "Asus Virtuoso 100/200 (Xonar)"
|
||||
depends on SND
|
||||
select SND_OXYGEN_LIB
|
||||
help
|
||||
Say Y here to include support for sound cards based on the
|
||||
Asus AV200 chip, i.e., Xonar D2 and Xonar D2X.
|
||||
Asus AV100/AV200 chips, i.e., Xonar D2, DX and D2X.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-virtuoso.
|
||||
|
|
|
@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \
|
|||
ac97/ \
|
||||
ali5451/ \
|
||||
au88x0/ \
|
||||
aw2/ \
|
||||
ca0106/ \
|
||||
cs46xx/ \
|
||||
cs5535audio/ \
|
||||
|
|
|
@ -114,10 +114,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd
|
|||
|
||||
static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static const char *texts[] = { "2ch", "4ch", "6ch" };
|
||||
if (kcontrol->private_value)
|
||||
return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */
|
||||
return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
|
||||
static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
|
||||
return ac97_enum_text_info(kcontrol, uinfo, texts,
|
||||
kcontrol->private_value);
|
||||
}
|
||||
|
||||
static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
|
@ -133,13 +132,8 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|||
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
|
||||
unsigned char mode = ucontrol->value.enumerated.item[0];
|
||||
|
||||
if (kcontrol->private_value) {
|
||||
if (mode >= 2)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (mode >= 3)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (mode >= kcontrol->private_value)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode != ac97->channel_mode) {
|
||||
ac97->channel_mode = mode;
|
||||
|
@ -158,6 +152,7 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|||
.get = ac97_surround_jack_mode_get, \
|
||||
.put = ac97_surround_jack_mode_put, \
|
||||
}
|
||||
/* 6ch */
|
||||
#define AC97_CHANNEL_MODE_CTL \
|
||||
{ \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
|
@ -165,7 +160,9 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|||
.info = ac97_channel_mode_info, \
|
||||
.get = ac97_channel_mode_get, \
|
||||
.put = ac97_channel_mode_put, \
|
||||
.private_value = 3, \
|
||||
}
|
||||
/* 4ch */
|
||||
#define AC97_CHANNEL_MODE_4CH_CTL \
|
||||
{ \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
|
@ -173,7 +170,17 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|||
.info = ac97_channel_mode_info, \
|
||||
.get = ac97_channel_mode_get, \
|
||||
.put = ac97_channel_mode_put, \
|
||||
.private_value = 1, \
|
||||
.private_value = 2, \
|
||||
}
|
||||
/* 8ch */
|
||||
#define AC97_CHANNEL_MODE_8CH_CTL \
|
||||
{ \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = "Channel Mode", \
|
||||
.info = ac97_channel_mode_info, \
|
||||
.get = ac97_channel_mode_get, \
|
||||
.put = ac97_channel_mode_put, \
|
||||
.private_value = 4, \
|
||||
}
|
||||
|
||||
static inline int is_surround_on(struct snd_ac97 *ac97)
|
||||
|
@ -210,6 +217,10 @@ static inline int is_shared_micin(struct snd_ac97 *ac97)
|
|||
return !ac97->indep_surround && !is_clfe_on(ac97);
|
||||
}
|
||||
|
||||
static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
|
||||
{
|
||||
return is_surround_on(ac97);
|
||||
}
|
||||
|
||||
/* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
|
||||
/* Modified for YMF743 by Keita Maehara <maehara@debian.org> */
|
||||
|
@ -2816,10 +2827,12 @@ static int patch_alc655(struct snd_ac97 * ac97)
|
|||
|
||||
#define AC97_ALC850_JACK_SELECT 0x76
|
||||
#define AC97_ALC850_MISC1 0x7a
|
||||
#define AC97_ALC850_MULTICH 0x6a
|
||||
|
||||
static void alc850_update_jacks(struct snd_ac97 *ac97)
|
||||
{
|
||||
int shared;
|
||||
int aux_is_back_surround;
|
||||
|
||||
/* shared Line-In / Surround Out */
|
||||
shared = is_shared_surrout(ac97);
|
||||
|
@ -2837,13 +2850,18 @@ static void alc850_update_jacks(struct snd_ac97 *ac97)
|
|||
/* MIC-IN = 1, CENTER-LFE = 5 */
|
||||
snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
|
||||
shared ? (5<<4) : (1<<4));
|
||||
|
||||
aux_is_back_surround = alc850_is_aux_back_surround(ac97);
|
||||
/* Aux is Back Surround */
|
||||
snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10,
|
||||
aux_is_back_surround ? (1<<10) : (0<<10));
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = {
|
||||
AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
|
||||
AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
|
||||
AC97_SURROUND_JACK_MODE_CTL,
|
||||
AC97_CHANNEL_MODE_CTL,
|
||||
AC97_CHANNEL_MODE_8CH_CTL,
|
||||
};
|
||||
|
||||
static int patch_alc850_specific(struct snd_ac97 *ac97)
|
||||
|
@ -2869,6 +2887,7 @@ static int patch_alc850(struct snd_ac97 *ac97)
|
|||
ac97->build_ops = &patch_alc850_ops;
|
||||
|
||||
ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */
|
||||
ac97->flags |= AC97_HAS_8CH;
|
||||
|
||||
/* assume only page 0 for writing cache */
|
||||
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR);
|
||||
|
@ -2878,6 +2897,7 @@ static int patch_alc850(struct snd_ac97 *ac97)
|
|||
spdif-in monitor off, spdif-in PCM off
|
||||
center on mic off, surround on line-in off
|
||||
duplicate front off
|
||||
NB default bit 10=0 = Aux is Capture, not Back Surround
|
||||
*/
|
||||
snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
|
||||
/* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off
|
||||
|
|
|
@ -574,7 +574,6 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
|
|||
r = rate > 48000;
|
||||
bus = pcm->bus;
|
||||
if (cfg == AC97_PCM_CFG_SPDIF) {
|
||||
int err;
|
||||
for (cidx = 0; cidx < 4; cidx++)
|
||||
if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) {
|
||||
err = set_spdif_rate(bus->codec[cidx], rate);
|
||||
|
|
|
@ -264,10 +264,10 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
|
|||
mdelay(1);
|
||||
if (!retry) {
|
||||
snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n",
|
||||
__FUNCTION__);
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
ad1889_debug("[%s] ready after %d ms\n", __FUNCTION__, 400 - retry);
|
||||
ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -854,8 +854,6 @@ snd_ad1889_free(struct snd_ad1889 *chip)
|
|||
|
||||
spin_unlock_irq(&chip->lock);
|
||||
|
||||
synchronize_irq(chip->irq);
|
||||
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
|
|
|
@ -1809,26 +1809,26 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ali *codec = kcontrol->private_data;
|
||||
unsigned int enable;
|
||||
unsigned int spdif_enable;
|
||||
|
||||
enable = ucontrol->value.integer.value[0] ? 1 : 0;
|
||||
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
|
||||
|
||||
spin_lock_irq(&codec->reg_lock);
|
||||
switch (kcontrol->private_value) {
|
||||
case 0:
|
||||
enable = (codec->spdif_mask & 0x02) ? 1 : 0;
|
||||
spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0;
|
||||
break;
|
||||
case 1:
|
||||
enable = ((codec->spdif_mask & 0x02) &&
|
||||
spdif_enable = ((codec->spdif_mask & 0x02) &&
|
||||
(codec->spdif_mask & 0x04)) ? 1 : 0;
|
||||
break;
|
||||
case 2:
|
||||
enable = (codec->spdif_mask & 0x01) ? 1 : 0;
|
||||
spdif_enable = (codec->spdif_mask & 0x01) ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ucontrol->value.integer.value[0] = enable;
|
||||
ucontrol->value.integer.value[0] = spdif_enable;
|
||||
spin_unlock_irq(&codec->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1837,17 +1837,17 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ali *codec = kcontrol->private_data;
|
||||
unsigned int change = 0, enable = 0;
|
||||
unsigned int change = 0, spdif_enable = 0;
|
||||
|
||||
enable = ucontrol->value.integer.value[0] ? 1 : 0;
|
||||
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
|
||||
|
||||
spin_lock_irq(&codec->reg_lock);
|
||||
switch (kcontrol->private_value) {
|
||||
case 0:
|
||||
change = (codec->spdif_mask & 0x02) ? 1 : 0;
|
||||
change = change ^ enable;
|
||||
change = change ^ spdif_enable;
|
||||
if (change) {
|
||||
if (enable) {
|
||||
if (spdif_enable) {
|
||||
codec->spdif_mask |= 0x02;
|
||||
snd_ali_enable_spdif_out(codec);
|
||||
} else {
|
||||
|
@ -1859,9 +1859,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
|
|||
break;
|
||||
case 1:
|
||||
change = (codec->spdif_mask & 0x04) ? 1 : 0;
|
||||
change = change ^ enable;
|
||||
change = change ^ spdif_enable;
|
||||
if (change && (codec->spdif_mask & 0x02)) {
|
||||
if (enable) {
|
||||
if (spdif_enable) {
|
||||
codec->spdif_mask |= 0x04;
|
||||
snd_ali_enable_spdif_chnout(codec);
|
||||
} else {
|
||||
|
@ -1872,9 +1872,9 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
|
|||
break;
|
||||
case 2:
|
||||
change = (codec->spdif_mask & 0x01) ? 1 : 0;
|
||||
change = change ^ enable;
|
||||
change = change ^ spdif_enable;
|
||||
if (change) {
|
||||
if (enable) {
|
||||
if (spdif_enable) {
|
||||
codec->spdif_mask |= 0x01;
|
||||
snd_ali_enable_spdif_in(codec);
|
||||
} else {
|
||||
|
@ -2047,10 +2047,8 @@ static int snd_ali_free(struct snd_ali * codec)
|
|||
{
|
||||
if (codec->hw_initialized)
|
||||
snd_ali_disable_address_interrupt(codec);
|
||||
if (codec->irq >= 0) {
|
||||
synchronize_irq(codec->irq);
|
||||
if (codec->irq >= 0)
|
||||
free_irq(codec->irq, codec);
|
||||
}
|
||||
if (codec->port)
|
||||
pci_release_regions(codec->pci);
|
||||
pci_disable_device(codec->pci);
|
||||
|
|
|
@ -92,8 +92,8 @@
|
|||
|
||||
#if DEBUG_CALLS
|
||||
#define snd_als300_dbgcalls(format, args...) printk(format, ##args)
|
||||
#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
|
||||
#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
|
||||
#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
|
||||
#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
|
||||
#else
|
||||
#define snd_als300_dbgcalls(format, args...)
|
||||
#define snd_als300_dbgcallenter()
|
||||
|
|
|
@ -1553,7 +1553,7 @@ static int snd_atiixp_free(struct atiixp *chip)
|
|||
if (chip->irq < 0)
|
||||
goto __hw_end;
|
||||
snd_atiixp_chip_stop(chip);
|
||||
synchronize_irq(chip->irq);
|
||||
|
||||
__hw_end:
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
|
|
@ -1197,7 +1197,7 @@ static int snd_atiixp_free(struct atiixp_modem *chip)
|
|||
if (chip->irq < 0)
|
||||
goto __hw_end;
|
||||
snd_atiixp_chip_stop(chip);
|
||||
synchronize_irq(chip->irq);
|
||||
|
||||
__hw_end:
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
|
|
@ -126,7 +126,6 @@ static int snd_vortex_dev_free(struct snd_device *device)
|
|||
vortex_gameport_unregister(vortex);
|
||||
vortex_core_shutdown(vortex);
|
||||
// Take down PCI interface.
|
||||
synchronize_irq(vortex->irq);
|
||||
free_irq(vortex->irq, vortex);
|
||||
iounmap(vortex->mmio);
|
||||
pci_release_regions(vortex->pci_dev);
|
||||
|
@ -220,7 +219,6 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
|
|||
return 0;
|
||||
|
||||
alloc_out:
|
||||
synchronize_irq(chip->irq);
|
||||
free_irq(chip->irq, chip);
|
||||
irq_out:
|
||||
vortex_core_shutdown(chip);
|
||||
|
|
|
@ -498,14 +498,14 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
|
|||
};
|
||||
|
||||
/* create a pcm device */
|
||||
static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
|
||||
static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_kcontrol *kctl;
|
||||
int i;
|
||||
int err, nr_capt;
|
||||
|
||||
if ((chip == 0) || (idx < 0) || (idx >= VORTEX_PCM_LAST))
|
||||
if (!chip || idx < 0 || idx >= VORTEX_PCM_LAST)
|
||||
return -ENODEV;
|
||||
|
||||
/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
|
||||
|
@ -514,9 +514,9 @@ static int __devinit snd_vortex_new_pcm(vortex_t * chip, int idx, int nr)
|
|||
nr_capt = nr;
|
||||
else
|
||||
nr_capt = 0;
|
||||
if ((err =
|
||||
snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
|
||||
nr_capt, &pcm)) < 0)
|
||||
err = snd_pcm_new(chip->card, vortex_pcm_prettyname[idx], idx, nr,
|
||||
nr_capt, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
strcpy(pcm->name, vortex_pcm_name[idx]);
|
||||
chip->pcm[idx] = pcm;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
|
||||
|
||||
obj-$(CONFIG_SND_AW2) += snd-aw2.o
|
|
@ -0,0 +1,794 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
|
||||
* Jean-Christian Hassler <jhassler@free.fr>
|
||||
*
|
||||
* This file is part of the Audiowerk2 ALSA driver
|
||||
*
|
||||
* The Audiowerk2 ALSA 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; version 2.
|
||||
*
|
||||
* The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/control.h>
|
||||
|
||||
#include "saa7146.h"
|
||||
#include "aw2-saa7146.h"
|
||||
|
||||
MODULE_AUTHOR("Cedric Bregardis <cedric.bregardis@free.fr>, "
|
||||
"Jean-Christian Hassler <jhassler@free.fr>");
|
||||
MODULE_DESCRIPTION("Emagic Audiowerk 2 sound driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*********************************
|
||||
* DEFINES
|
||||
********************************/
|
||||
#define PCI_VENDOR_ID_SAA7146 0x1131
|
||||
#define PCI_DEVICE_ID_SAA7146 0x7146
|
||||
|
||||
#define CTL_ROUTE_ANALOG 0
|
||||
#define CTL_ROUTE_DIGITAL 1
|
||||
|
||||
/*********************************
|
||||
* TYPEDEFS
|
||||
********************************/
|
||||
/* hardware definition */
|
||||
static struct snd_pcm_hardware snd_aw2_playback_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_44100,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 4,
|
||||
.buffer_bytes_max = 32768,
|
||||
.period_bytes_min = 4096,
|
||||
.period_bytes_max = 32768,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware snd_aw2_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_44100,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 44100,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 32768,
|
||||
.period_bytes_min = 4096,
|
||||
.period_bytes_max = 32768,
|
||||
.periods_min = 1,
|
||||
.periods_max = 1024,
|
||||
};
|
||||
|
||||
struct aw2_pcm_device {
|
||||
struct snd_pcm *pcm;
|
||||
unsigned int stream_number;
|
||||
struct aw2 *chip;
|
||||
};
|
||||
|
||||
struct aw2 {
|
||||
struct snd_aw2_saa7146 saa7146;
|
||||
|
||||
struct pci_dev *pci;
|
||||
int irq;
|
||||
spinlock_t reg_lock;
|
||||
struct mutex mtx;
|
||||
|
||||
unsigned long iobase_phys;
|
||||
void __iomem *iobase_virt;
|
||||
|
||||
struct snd_card *card;
|
||||
|
||||
struct aw2_pcm_device device_playback[NB_STREAM_PLAYBACK];
|
||||
struct aw2_pcm_device device_capture[NB_STREAM_CAPTURE];
|
||||
};
|
||||
|
||||
/*********************************
|
||||
* FUNCTION DECLARATIONS
|
||||
********************************/
|
||||
static int __init alsa_card_aw2_init(void);
|
||||
static void __exit alsa_card_aw2_exit(void);
|
||||
static int snd_aw2_dev_free(struct snd_device *device);
|
||||
static int __devinit snd_aw2_create(struct snd_card *card,
|
||||
struct pci_dev *pci, struct aw2 **rchip);
|
||||
static int __devinit snd_aw2_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id);
|
||||
static void __devexit snd_aw2_remove(struct pci_dev *pci);
|
||||
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params);
|
||||
static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream);
|
||||
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
|
||||
int cmd);
|
||||
static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
|
||||
*substream);
|
||||
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
|
||||
*substream);
|
||||
static int __devinit snd_aw2_new_pcm(struct aw2 *chip);
|
||||
|
||||
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo);
|
||||
static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value
|
||||
*ucontrol);
|
||||
static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value
|
||||
*ucontrol);
|
||||
|
||||
/*********************************
|
||||
* VARIABLES
|
||||
********************************/
|
||||
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 Audiowerk2 soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard.");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard.");
|
||||
|
||||
static struct pci_device_id snd_aw2_ids[] = {
|
||||
{PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0, 0},
|
||||
{0}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
|
||||
|
||||
/* pci_driver definition */
|
||||
static struct pci_driver driver = {
|
||||
.name = "Emagic Audiowerk 2",
|
||||
.id_table = snd_aw2_ids,
|
||||
.probe = snd_aw2_probe,
|
||||
.remove = __devexit_p(snd_aw2_remove),
|
||||
};
|
||||
|
||||
/* operators for playback PCM alsa interface */
|
||||
static struct snd_pcm_ops snd_aw2_playback_ops = {
|
||||
.open = snd_aw2_pcm_playback_open,
|
||||
.close = snd_aw2_pcm_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_aw2_pcm_hw_params,
|
||||
.hw_free = snd_aw2_pcm_hw_free,
|
||||
.prepare = snd_aw2_pcm_prepare_playback,
|
||||
.trigger = snd_aw2_pcm_trigger_playback,
|
||||
.pointer = snd_aw2_pcm_pointer_playback,
|
||||
};
|
||||
|
||||
/* operators for capture PCM alsa interface */
|
||||
static struct snd_pcm_ops snd_aw2_capture_ops = {
|
||||
.open = snd_aw2_pcm_capture_open,
|
||||
.close = snd_aw2_pcm_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_aw2_pcm_hw_params,
|
||||
.hw_free = snd_aw2_pcm_hw_free,
|
||||
.prepare = snd_aw2_pcm_prepare_capture,
|
||||
.trigger = snd_aw2_pcm_trigger_capture,
|
||||
.pointer = snd_aw2_pcm_pointer_capture,
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new aw2_control __devinitdata = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "PCM Capture Route",
|
||||
.index = 0,
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.private_value = 0xffff,
|
||||
.info = snd_aw2_control_switch_capture_info,
|
||||
.get = snd_aw2_control_switch_capture_get,
|
||||
.put = snd_aw2_control_switch_capture_put
|
||||
};
|
||||
|
||||
/*********************************
|
||||
* FUNCTION IMPLEMENTATIONS
|
||||
********************************/
|
||||
|
||||
/* initialization of the module */
|
||||
static int __init alsa_card_aw2_init(void)
|
||||
{
|
||||
snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
|
||||
return pci_register_driver(&driver);
|
||||
}
|
||||
|
||||
/* clean up the module */
|
||||
static void __exit alsa_card_aw2_exit(void)
|
||||
{
|
||||
snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
|
||||
pci_unregister_driver(&driver);
|
||||
}
|
||||
|
||||
module_init(alsa_card_aw2_init);
|
||||
module_exit(alsa_card_aw2_exit);
|
||||
|
||||
/* component-destructor */
|
||||
static int snd_aw2_dev_free(struct snd_device *device)
|
||||
{
|
||||
struct aw2 *chip = device->device_data;
|
||||
|
||||
/* Free hardware */
|
||||
snd_aw2_saa7146_free(&chip->saa7146);
|
||||
|
||||
/* release the irq */
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, (void *)chip);
|
||||
/* release the i/o ports & memory */
|
||||
if (chip->iobase_virt)
|
||||
iounmap(chip->iobase_virt);
|
||||
|
||||
pci_release_regions(chip->pci);
|
||||
/* disable the PCI entry */
|
||||
pci_disable_device(chip->pci);
|
||||
/* release the data */
|
||||
kfree(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* chip-specific constructor */
|
||||
static int __devinit snd_aw2_create(struct snd_card *card,
|
||||
struct pci_dev *pci, struct aw2 **rchip)
|
||||
{
|
||||
struct aw2 *chip;
|
||||
int err;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_aw2_dev_free,
|
||||
};
|
||||
|
||||
*rchip = NULL;
|
||||
|
||||
/* initialize the PCI entry */
|
||||
err = pci_enable_device(pci);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pci_set_master(pci);
|
||||
|
||||
/* check PCI availability (32bit DMA) */
|
||||
if ((pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) ||
|
||||
(pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0)) {
|
||||
printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n");
|
||||
pci_disable_device(pci);
|
||||
return -ENXIO;
|
||||
}
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL) {
|
||||
pci_disable_device(pci);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* initialize the stuff */
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
|
||||
/* (1) PCI resource allocation */
|
||||
err = pci_request_regions(pci, "Audiowerk2");
|
||||
if (err < 0) {
|
||||
pci_disable_device(pci);
|
||||
kfree(chip);
|
||||
return err;
|
||||
}
|
||||
chip->iobase_phys = pci_resource_start(pci, 0);
|
||||
chip->iobase_virt =
|
||||
ioremap_nocache(chip->iobase_phys,
|
||||
pci_resource_len(pci, 0));
|
||||
|
||||
if (chip->iobase_virt == NULL) {
|
||||
printk(KERN_ERR "aw2: unable to remap memory region");
|
||||
pci_release_regions(pci);
|
||||
pci_disable_device(pci);
|
||||
kfree(chip);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
|
||||
IRQF_SHARED, "Audiowerk2", chip)) {
|
||||
printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
|
||||
|
||||
iounmap(chip->iobase_virt);
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
return -EBUSY;
|
||||
}
|
||||
chip->irq = pci->irq;
|
||||
|
||||
/* (2) initialization of the chip hardware */
|
||||
snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||
if (err < 0) {
|
||||
free_irq(chip->irq, (void *)chip);
|
||||
iounmap(chip->iobase_virt);
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
return err;
|
||||
}
|
||||
|
||||
snd_card_set_dev(card, &pci->dev);
|
||||
*rchip = chip;
|
||||
|
||||
printk(KERN_INFO
|
||||
"Audiowerk 2 sound card (saa7146 chipset) detected and "
|
||||
"managed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* constructor */
|
||||
static int __devinit snd_aw2_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
struct snd_card *card;
|
||||
struct aw2 *chip;
|
||||
int err;
|
||||
|
||||
/* (1) Continue if device is not enabled, else inc dev */
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
if (!enable[dev]) {
|
||||
dev++;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* (2) Create card instance */
|
||||
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
|
||||
if (card == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* (3) Create main component */
|
||||
err = snd_aw2_create(card, pci, &chip);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* initialize mutex */
|
||||
mutex_init(&chip->mtx);
|
||||
/* init spinlock */
|
||||
spin_lock_init(&chip->reg_lock);
|
||||
/* (4) Define driver ID and name string */
|
||||
strcpy(card->driver, "aw2");
|
||||
strcpy(card->shortname, "Audiowerk2");
|
||||
|
||||
sprintf(card->longname, "%s with SAA7146 irq %i",
|
||||
card->shortname, chip->irq);
|
||||
|
||||
/* (5) Create other components */
|
||||
snd_aw2_new_pcm(chip);
|
||||
|
||||
/* (6) Register card instance */
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* (7) Set PCI driver data */
|
||||
pci_set_drvdata(pci, card);
|
||||
|
||||
dev++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* destructor */
|
||||
static void __devexit snd_aw2_remove(struct pci_dev *pci)
|
||||
{
|
||||
snd_card_free(pci_get_drvdata(pci));
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
/* open callback */
|
||||
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_printdd(KERN_DEBUG "aw2: Playback_open \n");
|
||||
runtime->hw = snd_aw2_playback_hw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close callback */
|
||||
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_printdd(KERN_DEBUG "aw2: Capture_open \n");
|
||||
runtime->hw = snd_aw2_capture_hw;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close callback */
|
||||
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
/* TODO: something to do ? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* hw_params callback */
|
||||
static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
return snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
}
|
||||
|
||||
/* hw_free callback */
|
||||
static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
/* prepare callback for playback */
|
||||
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned long period_size, buffer_size;
|
||||
|
||||
mutex_lock(&chip->mtx);
|
||||
|
||||
period_size = snd_pcm_lib_period_bytes(substream);
|
||||
buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
|
||||
snd_aw2_saa7146_pcm_init_playback(&chip->saa7146,
|
||||
pcm_device->stream_number,
|
||||
runtime->dma_addr, period_size,
|
||||
buffer_size);
|
||||
|
||||
/* Define Interrupt callback */
|
||||
snd_aw2_saa7146_define_it_playback_callback(pcm_device->stream_number,
|
||||
(snd_aw2_saa7146_it_cb)
|
||||
snd_pcm_period_elapsed,
|
||||
(void *)substream);
|
||||
|
||||
mutex_unlock(&chip->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* prepare callback for capture */
|
||||
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned long period_size, buffer_size;
|
||||
|
||||
mutex_lock(&chip->mtx);
|
||||
|
||||
period_size = snd_pcm_lib_period_bytes(substream);
|
||||
buffer_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
|
||||
snd_aw2_saa7146_pcm_init_capture(&chip->saa7146,
|
||||
pcm_device->stream_number,
|
||||
runtime->dma_addr, period_size,
|
||||
buffer_size);
|
||||
|
||||
/* Define Interrupt callback */
|
||||
snd_aw2_saa7146_define_it_capture_callback(pcm_device->stream_number,
|
||||
(snd_aw2_saa7146_it_cb)
|
||||
snd_pcm_period_elapsed,
|
||||
(void *)substream);
|
||||
|
||||
mutex_unlock(&chip->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* playback trigger callback */
|
||||
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
int status = 0;
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
spin_lock(&chip->reg_lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146,
|
||||
pcm_device->
|
||||
stream_number);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_aw2_saa7146_pcm_trigger_stop_playback(&chip->saa7146,
|
||||
pcm_device->
|
||||
stream_number);
|
||||
break;
|
||||
default:
|
||||
status = -EINVAL;
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* capture trigger callback */
|
||||
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
int status = 0;
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
spin_lock(&chip->reg_lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146,
|
||||
pcm_device->
|
||||
stream_number);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_aw2_saa7146_pcm_trigger_stop_capture(&chip->saa7146,
|
||||
pcm_device->
|
||||
stream_number);
|
||||
break;
|
||||
default:
|
||||
status = -EINVAL;
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* playback pointer callback */
|
||||
static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
|
||||
*substream)
|
||||
{
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
unsigned int current_ptr;
|
||||
|
||||
/* get the current hardware pointer */
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
current_ptr =
|
||||
snd_aw2_saa7146_get_hw_ptr_playback(&chip->saa7146,
|
||||
pcm_device->stream_number,
|
||||
runtime->dma_area,
|
||||
runtime->buffer_size);
|
||||
|
||||
return bytes_to_frames(substream->runtime, current_ptr);
|
||||
}
|
||||
|
||||
/* capture pointer callback */
|
||||
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
|
||||
*substream)
|
||||
{
|
||||
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
|
||||
struct aw2 *chip = pcm_device->chip;
|
||||
unsigned int current_ptr;
|
||||
|
||||
/* get the current hardware pointer */
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
current_ptr =
|
||||
snd_aw2_saa7146_get_hw_ptr_capture(&chip->saa7146,
|
||||
pcm_device->stream_number,
|
||||
runtime->dma_area,
|
||||
runtime->buffer_size);
|
||||
|
||||
return bytes_to_frames(substream->runtime, current_ptr);
|
||||
}
|
||||
|
||||
/* create a pcm device */
|
||||
static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
|
||||
{
|
||||
struct snd_pcm *pcm_playback_ana;
|
||||
struct snd_pcm *pcm_playback_num;
|
||||
struct snd_pcm *pcm_capture;
|
||||
struct aw2_pcm_device *pcm_device;
|
||||
int err = 0;
|
||||
|
||||
/* Create new Alsa PCM device */
|
||||
|
||||
err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0,
|
||||
&pcm_playback_ana);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Creation ok */
|
||||
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA];
|
||||
|
||||
/* Set PCM device name */
|
||||
strcpy(pcm_playback_ana->name, "Analog playback");
|
||||
/* Associate private data to PCM device */
|
||||
pcm_playback_ana->private_data = pcm_device;
|
||||
/* set operators of PCM device */
|
||||
snd_pcm_set_ops(pcm_playback_ana, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&snd_aw2_playback_ops);
|
||||
/* store PCM device */
|
||||
pcm_device->pcm = pcm_playback_ana;
|
||||
/* give base chip pointer to our internal pcm device
|
||||
structure */
|
||||
pcm_device->chip = chip;
|
||||
/* Give stream number to PCM device */
|
||||
pcm_device->stream_number = NUM_STREAM_PLAYBACK_ANA;
|
||||
|
||||
/* pre-allocation of buffers */
|
||||
/* Preallocate continuous pages. */
|
||||
err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data
|
||||
(chip->pci),
|
||||
64 * 1024, 64 * 1024);
|
||||
if (err)
|
||||
printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all "
|
||||
"error (0x%X)\n", err);
|
||||
|
||||
err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
|
||||
&pcm_playback_num);
|
||||
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
|
||||
return err;
|
||||
}
|
||||
/* Creation ok */
|
||||
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG];
|
||||
|
||||
/* Set PCM device name */
|
||||
strcpy(pcm_playback_num->name, "Digital playback");
|
||||
/* Associate private data to PCM device */
|
||||
pcm_playback_num->private_data = pcm_device;
|
||||
/* set operators of PCM device */
|
||||
snd_pcm_set_ops(pcm_playback_num, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&snd_aw2_playback_ops);
|
||||
/* store PCM device */
|
||||
pcm_device->pcm = pcm_playback_num;
|
||||
/* give base chip pointer to our internal pcm device
|
||||
structure */
|
||||
pcm_device->chip = chip;
|
||||
/* Give stream number to PCM device */
|
||||
pcm_device->stream_number = NUM_STREAM_PLAYBACK_DIG;
|
||||
|
||||
/* pre-allocation of buffers */
|
||||
/* Preallocate continuous pages. */
|
||||
err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data
|
||||
(chip->pci),
|
||||
64 * 1024, 64 * 1024);
|
||||
if (err)
|
||||
printk(KERN_ERR
|
||||
"aw2: snd_pcm_lib_preallocate_pages_for_all error "
|
||||
"(0x%X)\n", err);
|
||||
|
||||
|
||||
|
||||
err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
|
||||
&pcm_capture);
|
||||
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Creation ok */
|
||||
pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA];
|
||||
|
||||
/* Set PCM device name */
|
||||
strcpy(pcm_capture->name, "Capture");
|
||||
/* Associate private data to PCM device */
|
||||
pcm_capture->private_data = pcm_device;
|
||||
/* set operators of PCM device */
|
||||
snd_pcm_set_ops(pcm_capture, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&snd_aw2_capture_ops);
|
||||
/* store PCM device */
|
||||
pcm_device->pcm = pcm_capture;
|
||||
/* give base chip pointer to our internal pcm device
|
||||
structure */
|
||||
pcm_device->chip = chip;
|
||||
/* Give stream number to PCM device */
|
||||
pcm_device->stream_number = NUM_STREAM_CAPTURE_ANA;
|
||||
|
||||
/* pre-allocation of buffers */
|
||||
/* Preallocate continuous pages. */
|
||||
err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data
|
||||
(chip->pci),
|
||||
64 * 1024, 64 * 1024);
|
||||
if (err)
|
||||
printk(KERN_ERR
|
||||
"aw2: snd_pcm_lib_preallocate_pages_for_all error "
|
||||
"(0x%X)\n", err);
|
||||
|
||||
|
||||
/* Create control */
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip));
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[2] = {
|
||||
"Analog", "Digital"
|
||||
};
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 2;
|
||||
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 snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value
|
||||
*ucontrol)
|
||||
{
|
||||
struct aw2 *chip = snd_kcontrol_chip(kcontrol);
|
||||
if (snd_aw2_saa7146_is_using_digital_input(&chip->saa7146))
|
||||
ucontrol->value.enumerated.item[0] = CTL_ROUTE_DIGITAL;
|
||||
else
|
||||
ucontrol->value.enumerated.item[0] = CTL_ROUTE_ANALOG;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value
|
||||
*ucontrol)
|
||||
{
|
||||
struct aw2 *chip = snd_kcontrol_chip(kcontrol);
|
||||
int changed = 0;
|
||||
int is_disgital =
|
||||
snd_aw2_saa7146_is_using_digital_input(&chip->saa7146);
|
||||
|
||||
if (((ucontrol->value.integer.value[0] == CTL_ROUTE_DIGITAL)
|
||||
&& !is_disgital)
|
||||
|| ((ucontrol->value.integer.value[0] == CTL_ROUTE_ANALOG)
|
||||
&& is_disgital)) {
|
||||
snd_aw2_saa7146_use_digital_input(&chip->saa7146, !is_disgital);
|
||||
changed = 1;
|
||||
}
|
||||
return changed;
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
|
||||
* Jean-Christian Hassler <jhassler@free.fr>
|
||||
*
|
||||
* This file is part of the Audiowerk2 ALSA driver
|
||||
*
|
||||
* The Audiowerk2 ALSA 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; version 2.
|
||||
*
|
||||
* The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define AW2_SAA7146_M
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "saa7146.h"
|
||||
#include "aw2-saa7146.h"
|
||||
|
||||
#include "aw2-tsl.c"
|
||||
|
||||
#define WRITEREG(value, addr) writel((value), chip->base_addr + (addr))
|
||||
#define READREG(addr) readl(chip->base_addr + (addr))
|
||||
|
||||
static struct snd_aw2_saa7146_cb_param
|
||||
arr_substream_it_playback_cb[NB_STREAM_PLAYBACK];
|
||||
static struct snd_aw2_saa7146_cb_param
|
||||
arr_substream_it_capture_cb[NB_STREAM_CAPTURE];
|
||||
|
||||
static int snd_aw2_saa7146_get_limit(int size);
|
||||
|
||||
/* chip-specific destructor */
|
||||
int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip)
|
||||
{
|
||||
/* disable all irqs */
|
||||
WRITEREG(0, IER);
|
||||
|
||||
/* reset saa7146 */
|
||||
WRITEREG((MRST_N << 16), MC1);
|
||||
|
||||
/* Unset base addr */
|
||||
chip->base_addr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
|
||||
void __iomem *pci_base_addr)
|
||||
{
|
||||
/* set PCI burst/threshold
|
||||
|
||||
Burst length definition
|
||||
VALUE BURST LENGTH
|
||||
000 1 Dword
|
||||
001 2 Dwords
|
||||
010 4 Dwords
|
||||
011 8 Dwords
|
||||
100 16 Dwords
|
||||
101 32 Dwords
|
||||
110 64 Dwords
|
||||
111 128 Dwords
|
||||
|
||||
Threshold definition
|
||||
VALUE WRITE MODE READ MODE
|
||||
00 1 Dword of valid data 1 empty Dword
|
||||
01 4 Dwords of valid data 4 empty Dwords
|
||||
10 8 Dwords of valid data 8 empty Dwords
|
||||
11 16 Dwords of valid data 16 empty Dwords */
|
||||
|
||||
unsigned int acon2;
|
||||
unsigned int acon1 = 0;
|
||||
int i;
|
||||
|
||||
/* Set base addr */
|
||||
chip->base_addr = pci_base_addr;
|
||||
|
||||
/* disable all irqs */
|
||||
WRITEREG(0, IER);
|
||||
|
||||
/* reset saa7146 */
|
||||
WRITEREG((MRST_N << 16), MC1);
|
||||
|
||||
/* enable audio interface */
|
||||
#ifdef __BIG_ENDIAN
|
||||
acon1 |= A1_SWAP;
|
||||
acon1 |= A2_SWAP;
|
||||
#endif
|
||||
/* WS0_CTRL, WS0_SYNC: input TSL1, I2S */
|
||||
|
||||
/* At initialization WS1 and WS2 are disbaled (configured as input */
|
||||
acon1 |= 0 * WS1_CTRL;
|
||||
acon1 |= 0 * WS2_CTRL;
|
||||
|
||||
/* WS4 is not used. So it must not restart A2.
|
||||
This is why it is configured as output (force to low) */
|
||||
acon1 |= 3 * WS4_CTRL;
|
||||
|
||||
/* WS3_CTRL, WS3_SYNC: output TSL2, I2S */
|
||||
acon1 |= 2 * WS3_CTRL;
|
||||
|
||||
/* A1 and A2 are active and asynchronous */
|
||||
acon1 |= 3 * AUDIO_MODE;
|
||||
WRITEREG(acon1, ACON1);
|
||||
|
||||
/* The following comes from original windows driver.
|
||||
It is needed to have a correct behavior of input and output
|
||||
simultenously, but I don't know why ! */
|
||||
WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) +
|
||||
3 * (BurstA1_out) + 3 * (ThreshA1_out) +
|
||||
3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A);
|
||||
|
||||
/* enable audio port pins */
|
||||
WRITEREG((EAP << 16) | EAP, MC1);
|
||||
|
||||
/* enable I2C */
|
||||
WRITEREG((EI2C << 16) | EI2C, MC1);
|
||||
/* enable interrupts */
|
||||
WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER);
|
||||
|
||||
/* audio configuration */
|
||||
acon2 = A2_CLKSRC | BCLK1_OEN;
|
||||
WRITEREG(acon2, ACON2);
|
||||
|
||||
/* By default use analog input */
|
||||
snd_aw2_saa7146_use_digital_input(chip, 0);
|
||||
|
||||
/* TSL setup */
|
||||
for (i = 0; i < 8; ++i) {
|
||||
WRITEREG(tsl1[i], TSL1 + (i * 4));
|
||||
WRITEREG(tsl2[i], TSL2 + (i * 4));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number,
|
||||
unsigned long dma_addr,
|
||||
unsigned long period_size,
|
||||
unsigned long buffer_size)
|
||||
{
|
||||
unsigned long dw_page, dw_limit;
|
||||
|
||||
/* Configure DMA for substream
|
||||
Configuration informations: ALSA has allocated continuous memory
|
||||
pages. So we don't need to use MMU of saa7146.
|
||||
*/
|
||||
|
||||
/* No MMU -> nothing to do with PageA1, we only configure the limit of
|
||||
PageAx_out register */
|
||||
/* Disable MMU */
|
||||
dw_page = (0L << 11);
|
||||
|
||||
/* Configure Limit for DMA access.
|
||||
The limit register defines an address limit, which generates
|
||||
an interrupt if passed by the actual PCI address pointer.
|
||||
'0001' means an interrupt will be generated if the lower
|
||||
6 bits (64 bytes) of the PCI address are zero. '0010'
|
||||
defines a limit of 128 bytes, '0011' one of 256 bytes, and
|
||||
so on up to 1 Mbyte defined by '1111'. This interrupt range
|
||||
can be calculated as follows:
|
||||
Range = 2^(5 + Limit) bytes.
|
||||
*/
|
||||
dw_limit = snd_aw2_saa7146_get_limit(period_size);
|
||||
dw_page |= (dw_limit << 4);
|
||||
|
||||
if (stream_number == 0) {
|
||||
WRITEREG(dw_page, PageA2_out);
|
||||
|
||||
/* Base address for DMA transfert. */
|
||||
/* This address has been reserved by ALSA. */
|
||||
/* This is a physical address */
|
||||
WRITEREG(dma_addr, BaseA2_out);
|
||||
|
||||
/* Define upper limit for DMA access */
|
||||
WRITEREG(dma_addr + buffer_size, ProtA2_out);
|
||||
|
||||
} else if (stream_number == 1) {
|
||||
WRITEREG(dw_page, PageA1_out);
|
||||
|
||||
/* Base address for DMA transfert. */
|
||||
/* This address has been reserved by ALSA. */
|
||||
/* This is a physical address */
|
||||
WRITEREG(dma_addr, BaseA1_out);
|
||||
|
||||
/* Define upper limit for DMA access */
|
||||
WRITEREG(dma_addr + buffer_size, ProtA1_out);
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"aw2: snd_aw2_saa7146_pcm_init_playback: "
|
||||
"Substream number is not 0 or 1 -> not managed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number, unsigned long dma_addr,
|
||||
unsigned long period_size,
|
||||
unsigned long buffer_size)
|
||||
{
|
||||
unsigned long dw_page, dw_limit;
|
||||
|
||||
/* Configure DMA for substream
|
||||
Configuration informations: ALSA has allocated continuous memory
|
||||
pages. So we don't need to use MMU of saa7146.
|
||||
*/
|
||||
|
||||
/* No MMU -> nothing to do with PageA1, we only configure the limit of
|
||||
PageAx_out register */
|
||||
/* Disable MMU */
|
||||
dw_page = (0L << 11);
|
||||
|
||||
/* Configure Limit for DMA access.
|
||||
The limit register defines an address limit, which generates
|
||||
an interrupt if passed by the actual PCI address pointer.
|
||||
'0001' means an interrupt will be generated if the lower
|
||||
6 bits (64 bytes) of the PCI address are zero. '0010'
|
||||
defines a limit of 128 bytes, '0011' one of 256 bytes, and
|
||||
so on up to 1 Mbyte defined by '1111'. This interrupt range
|
||||
can be calculated as follows:
|
||||
Range = 2^(5 + Limit) bytes.
|
||||
*/
|
||||
dw_limit = snd_aw2_saa7146_get_limit(period_size);
|
||||
dw_page |= (dw_limit << 4);
|
||||
|
||||
if (stream_number == 0) {
|
||||
WRITEREG(dw_page, PageA1_in);
|
||||
|
||||
/* Base address for DMA transfert. */
|
||||
/* This address has been reserved by ALSA. */
|
||||
/* This is a physical address */
|
||||
WRITEREG(dma_addr, BaseA1_in);
|
||||
|
||||
/* Define upper limit for DMA access */
|
||||
WRITEREG(dma_addr + buffer_size, ProtA1_in);
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"aw2: snd_aw2_saa7146_pcm_init_capture: "
|
||||
"Substream number is not 0 -> not managed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number,
|
||||
snd_aw2_saa7146_it_cb
|
||||
p_it_callback,
|
||||
void *p_callback_param)
|
||||
{
|
||||
if (stream_number < NB_STREAM_PLAYBACK) {
|
||||
arr_substream_it_playback_cb[stream_number].p_it_callback =
|
||||
(snd_aw2_saa7146_it_cb) p_it_callback;
|
||||
arr_substream_it_playback_cb[stream_number].p_callback_param =
|
||||
(void *)p_callback_param;
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number,
|
||||
snd_aw2_saa7146_it_cb
|
||||
p_it_callback,
|
||||
void *p_callback_param)
|
||||
{
|
||||
if (stream_number < NB_STREAM_CAPTURE) {
|
||||
arr_substream_it_capture_cb[stream_number].p_it_callback =
|
||||
(snd_aw2_saa7146_it_cb) p_it_callback;
|
||||
arr_substream_it_capture_cb[stream_number].p_callback_param =
|
||||
(void *)p_callback_param;
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number)
|
||||
{
|
||||
unsigned int acon1 = 0;
|
||||
/* In aw8 driver, dma transfert is always active. It is
|
||||
started and stopped in a larger "space" */
|
||||
acon1 = READREG(ACON1);
|
||||
if (stream_number == 0) {
|
||||
WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1);
|
||||
|
||||
/* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
|
||||
acon1 |= 2 * WS2_CTRL;
|
||||
WRITEREG(acon1, ACON1);
|
||||
|
||||
} else if (stream_number == 1) {
|
||||
WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1);
|
||||
|
||||
/* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
|
||||
acon1 |= 1 * WS1_CTRL;
|
||||
WRITEREG(acon1, ACON1);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number)
|
||||
{
|
||||
unsigned int acon1 = 0;
|
||||
acon1 = READREG(ACON1);
|
||||
if (stream_number == 0) {
|
||||
/* WS2_CTRL, WS2_SYNC: output TSL2, I2S */
|
||||
acon1 &= ~(3 * WS2_CTRL);
|
||||
WRITEREG(acon1, ACON1);
|
||||
|
||||
WRITEREG((TR_E_A2_OUT << 16), MC1);
|
||||
} else if (stream_number == 1) {
|
||||
/* WS1_CTRL, WS1_SYNC: output TSL1, I2S */
|
||||
acon1 &= ~(3 * WS1_CTRL);
|
||||
WRITEREG(acon1, ACON1);
|
||||
|
||||
WRITEREG((TR_E_A1_OUT << 16), MC1);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number)
|
||||
{
|
||||
/* In aw8 driver, dma transfert is always active. It is
|
||||
started and stopped in a larger "space" */
|
||||
if (stream_number == 0)
|
||||
WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1);
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number)
|
||||
{
|
||||
if (stream_number == 0)
|
||||
WRITEREG((TR_E_A1_IN << 16), MC1);
|
||||
}
|
||||
|
||||
irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
unsigned int isr;
|
||||
unsigned int iicsta;
|
||||
struct snd_aw2_saa7146 *chip = dev_id;
|
||||
|
||||
isr = READREG(ISR);
|
||||
if (!isr)
|
||||
return IRQ_NONE;
|
||||
|
||||
WRITEREG(isr, ISR);
|
||||
|
||||
if (isr & (IIC_S | IIC_E)) {
|
||||
iicsta = READREG(IICSTA);
|
||||
WRITEREG(0x100, IICSTA);
|
||||
}
|
||||
|
||||
if (isr & A1_out) {
|
||||
if (arr_substream_it_playback_cb[1].p_it_callback != NULL) {
|
||||
arr_substream_it_playback_cb[1].
|
||||
p_it_callback(arr_substream_it_playback_cb[1].
|
||||
p_callback_param);
|
||||
}
|
||||
}
|
||||
if (isr & A2_out) {
|
||||
if (arr_substream_it_playback_cb[0].p_it_callback != NULL) {
|
||||
arr_substream_it_playback_cb[0].
|
||||
p_it_callback(arr_substream_it_playback_cb[0].
|
||||
p_callback_param);
|
||||
}
|
||||
|
||||
}
|
||||
if (isr & A1_in) {
|
||||
if (arr_substream_it_capture_cb[0].p_it_callback != NULL) {
|
||||
arr_substream_it_capture_cb[0].
|
||||
p_it_callback(arr_substream_it_capture_cb[0].
|
||||
p_callback_param);
|
||||
}
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number,
|
||||
unsigned char *start_addr,
|
||||
unsigned int buffer_size)
|
||||
{
|
||||
long pci_adp = 0;
|
||||
size_t ptr = 0;
|
||||
|
||||
if (stream_number == 0) {
|
||||
pci_adp = READREG(PCI_ADP3);
|
||||
ptr = pci_adp - (long)start_addr;
|
||||
|
||||
if (ptr == buffer_size)
|
||||
ptr = 0;
|
||||
}
|
||||
if (stream_number == 1) {
|
||||
pci_adp = READREG(PCI_ADP1);
|
||||
ptr = pci_adp - (size_t) start_addr;
|
||||
|
||||
if (ptr == buffer_size)
|
||||
ptr = 0;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number,
|
||||
unsigned char *start_addr,
|
||||
unsigned int buffer_size)
|
||||
{
|
||||
size_t pci_adp = 0;
|
||||
size_t ptr = 0;
|
||||
if (stream_number == 0) {
|
||||
pci_adp = READREG(PCI_ADP2);
|
||||
ptr = pci_adp - (size_t) start_addr;
|
||||
|
||||
if (ptr == buffer_size)
|
||||
ptr = 0;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
|
||||
int use_digital)
|
||||
{
|
||||
/* FIXME: switch between analog and digital input does not always work.
|
||||
It can produce a kind of white noise. It seams that received data
|
||||
are inverted sometime (endian inversion). Why ? I don't know, maybe
|
||||
a problem of synchronization... However for the time being I have
|
||||
not found the problem. Workaround: switch again (and again) between
|
||||
digital and analog input until it works. */
|
||||
if (use_digital)
|
||||
WRITEREG(0x40, GPIO_CTRL);
|
||||
else
|
||||
WRITEREG(0x50, GPIO_CTRL);
|
||||
}
|
||||
|
||||
int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip)
|
||||
{
|
||||
unsigned int reg_val = READREG(GPIO_CTRL);
|
||||
if ((reg_val & 0xFF) == 0x40)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_aw2_saa7146_get_limit(int size)
|
||||
{
|
||||
int limitsize = 32;
|
||||
int limit = 0;
|
||||
while (limitsize < size) {
|
||||
limitsize *= 2;
|
||||
limit++;
|
||||
}
|
||||
return limit;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
|
||||
* Jean-Christian Hassler <jhassler@free.fr>
|
||||
*
|
||||
* This file is part of the Audiowerk2 ALSA driver
|
||||
*
|
||||
* The Audiowerk2 ALSA 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; version 2.
|
||||
*
|
||||
* The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef AW2_SAA7146_H
|
||||
#define AW2_SAA7146_H
|
||||
|
||||
#define NB_STREAM_PLAYBACK 2
|
||||
#define NB_STREAM_CAPTURE 1
|
||||
|
||||
#define NUM_STREAM_PLAYBACK_ANA 0
|
||||
#define NUM_STREAM_PLAYBACK_DIG 1
|
||||
|
||||
#define NUM_STREAM_CAPTURE_ANA 0
|
||||
|
||||
typedef void (*snd_aw2_saa7146_it_cb) (void *);
|
||||
|
||||
struct snd_aw2_saa7146_cb_param {
|
||||
snd_aw2_saa7146_it_cb p_it_callback;
|
||||
void *p_callback_param;
|
||||
};
|
||||
|
||||
/* definition of the chip-specific record */
|
||||
|
||||
struct snd_aw2_saa7146 {
|
||||
void __iomem *base_addr;
|
||||
};
|
||||
|
||||
extern void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip,
|
||||
void __iomem *pci_base_addr);
|
||||
extern int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip);
|
||||
|
||||
extern void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number,
|
||||
unsigned long dma_addr,
|
||||
unsigned long period_size,
|
||||
unsigned long buffer_size);
|
||||
extern void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
|
||||
int stream_number,
|
||||
unsigned long dma_addr,
|
||||
unsigned long period_size,
|
||||
unsigned long buffer_size);
|
||||
extern void snd_aw2_saa7146_define_it_playback_callback(unsigned int
|
||||
stream_number,
|
||||
snd_aw2_saa7146_it_cb
|
||||
p_it_callback,
|
||||
void *p_callback_param);
|
||||
extern void snd_aw2_saa7146_define_it_capture_callback(unsigned int
|
||||
stream_number,
|
||||
snd_aw2_saa7146_it_cb
|
||||
p_it_callback,
|
||||
void *p_callback_param);
|
||||
extern void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146
|
||||
*chip, int stream_number);
|
||||
extern void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146
|
||||
*chip, int stream_number);
|
||||
|
||||
extern void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146
|
||||
*chip,
|
||||
int stream_number);
|
||||
extern void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146
|
||||
*chip, int stream_number);
|
||||
|
||||
extern irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id);
|
||||
extern unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146
|
||||
*chip,
|
||||
int stream_number,
|
||||
unsigned char
|
||||
*start_addr,
|
||||
unsigned int
|
||||
buffer_size);
|
||||
extern unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146
|
||||
*chip,
|
||||
int stream_number,
|
||||
unsigned char
|
||||
*start_addr,
|
||||
unsigned int
|
||||
buffer_size);
|
||||
|
||||
extern void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip,
|
||||
int use_digital);
|
||||
|
||||
extern int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146
|
||||
*chip);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,110 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
|
||||
* Jean-Christian Hassler <jhassler@free.fr>
|
||||
* Copyright 1998 Emagic Soft- und Hardware GmbH
|
||||
* Copyright 2002 Martijn Sipkema
|
||||
*
|
||||
* This file is part of the Audiowerk2 ALSA driver
|
||||
*
|
||||
* The Audiowerk2 ALSA 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; version 2.
|
||||
*
|
||||
* The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define TSL_WS0 (1UL << 31)
|
||||
#define TSL_WS1 (1UL << 30)
|
||||
#define TSL_WS2 (1UL << 29)
|
||||
#define TSL_WS3 (1UL << 28)
|
||||
#define TSL_WS4 (1UL << 27)
|
||||
#define TSL_DIS_A1 (1UL << 24)
|
||||
#define TSL_SDW_A1 (1UL << 23)
|
||||
#define TSL_SIB_A1 (1UL << 22)
|
||||
#define TSL_SF_A1 (1UL << 21)
|
||||
#define TSL_LF_A1 (1UL << 20)
|
||||
#define TSL_BSEL_A1 (1UL << 17)
|
||||
#define TSL_DOD_A1 (1UL << 15)
|
||||
#define TSL_LOW_A1 (1UL << 14)
|
||||
#define TSL_DIS_A2 (1UL << 11)
|
||||
#define TSL_SDW_A2 (1UL << 10)
|
||||
#define TSL_SIB_A2 (1UL << 9)
|
||||
#define TSL_SF_A2 (1UL << 8)
|
||||
#define TSL_LF_A2 (1UL << 7)
|
||||
#define TSL_BSEL_A2 (1UL << 4)
|
||||
#define TSL_DOD_A2 (1UL << 2)
|
||||
#define TSL_LOW_A2 (1UL << 1)
|
||||
#define TSL_EOS (1UL << 0)
|
||||
|
||||
/* Audiowerk8 hardware setup: */
|
||||
/* WS0, SD4, TSL1 - Analog/ digital in */
|
||||
/* WS1, SD0, TSL1 - Analog out #1, digital out */
|
||||
/* WS2, SD2, TSL1 - Analog out #2 */
|
||||
/* WS3, SD1, TSL2 - Analog out #3 */
|
||||
/* WS4, SD3, TSL2 - Analog out #4 */
|
||||
|
||||
/* Audiowerk8 timing: */
|
||||
/* Timeslot: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... */
|
||||
|
||||
/* A1_INPUT: */
|
||||
/* SD4: <_ADC-L_>-------<_ADC-R_>-------< */
|
||||
/* WS0: _______________/---------------\_ */
|
||||
|
||||
/* A1_OUTPUT: */
|
||||
/* SD0: <_1-L___>-------<_1-R___>-------< */
|
||||
/* WS1: _______________/---------------\_ */
|
||||
/* SD2: >-------<_2-L___>-------<_2-R___> */
|
||||
/* WS2: -------\_______________/--------- */
|
||||
|
||||
/* A2_OUTPUT: */
|
||||
/* SD1: <_3-L___>-------<_3-R___>-------< */
|
||||
/* WS3: _______________/---------------\_ */
|
||||
/* SD3: >-------<_4-L___>-------<_4-R___> */
|
||||
/* WS4: -------\_______________/--------- */
|
||||
|
||||
static int tsl1[8] = {
|
||||
1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1,
|
||||
|
||||
1 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
|
||||
|
||||
0 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
|
||||
|
||||
0 * TSL_SDW_A1 | 2 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1,
|
||||
|
||||
1 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
|
||||
|
||||
1 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
|
||||
|
||||
0 * TSL_SDW_A1 | 1 * TSL_BSEL_A1 |
|
||||
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0,
|
||||
|
||||
0 * TSL_SDW_A1 | 0 * TSL_BSEL_A1 | 0 * TSL_DIS_A1 |
|
||||
0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS,
|
||||
};
|
||||
|
||||
static int tsl2[8] = {
|
||||
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2,
|
||||
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
|
||||
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
|
||||
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
|
||||
0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
|
||||
0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
|
||||
0 * TSL_SDW_A2 | 1 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2,
|
||||
0 * TSL_SDW_A2 | 0 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_WS2 | TSL_EOS
|
||||
};
|
|
@ -0,0 +1,168 @@
|
|||
/*****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
|
||||
* Jean-Christian Hassler <jhassler@free.fr>
|
||||
*
|
||||
* This file is part of the Audiowerk2 ALSA driver
|
||||
*
|
||||
* The Audiowerk2 ALSA 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; version 2.
|
||||
*
|
||||
* The Audiowerk2 ALSA 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 the Audiowerk2 ALSA driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/* SAA7146 registers */
|
||||
#define PCI_BT_A 0x4C
|
||||
#define IICTFR 0x8C
|
||||
#define IICSTA 0x90
|
||||
#define BaseA1_in 0x94
|
||||
#define ProtA1_in 0x98
|
||||
#define PageA1_in 0x9C
|
||||
#define BaseA1_out 0xA0
|
||||
#define ProtA1_out 0xA4
|
||||
#define PageA1_out 0xA8
|
||||
#define BaseA2_in 0xAC
|
||||
#define ProtA2_in 0xB0
|
||||
#define PageA2_in 0xB4
|
||||
#define BaseA2_out 0xB8
|
||||
#define ProtA2_out 0xBC
|
||||
#define PageA2_out 0xC0
|
||||
#define IER 0xDC
|
||||
#define GPIO_CTRL 0xE0
|
||||
#define ACON1 0xF4
|
||||
#define ACON2 0xF8
|
||||
#define MC1 0xFC
|
||||
#define MC2 0x100
|
||||
#define ISR 0x10C
|
||||
#define PSR 0x110
|
||||
#define SSR 0x114
|
||||
#define PCI_ADP1 0x12C
|
||||
#define PCI_ADP2 0x130
|
||||
#define PCI_ADP3 0x134
|
||||
#define PCI_ADP4 0x138
|
||||
#define LEVEL_REP 0x140
|
||||
#define FB_BUFFER1 0x144
|
||||
#define FB_BUFFER2 0x148
|
||||
#define TSL1 0x180
|
||||
#define TSL2 0x1C0
|
||||
|
||||
#define ME (1UL << 11)
|
||||
#define LIMIT (1UL << 4)
|
||||
#define PV (1UL << 3)
|
||||
|
||||
/* PSR/ISR/IER */
|
||||
#define PPEF (1UL << 31)
|
||||
#define PABO (1UL << 30)
|
||||
#define IIC_S (1UL << 17)
|
||||
#define IIC_E (1UL << 16)
|
||||
#define A2_in (1UL << 15)
|
||||
#define A2_out (1UL << 14)
|
||||
#define A1_in (1UL << 13)
|
||||
#define A1_out (1UL << 12)
|
||||
#define AFOU (1UL << 11)
|
||||
#define PIN3 (1UL << 6)
|
||||
#define PIN2 (1UL << 5)
|
||||
#define PIN1 (1UL << 4)
|
||||
#define PIN0 (1UL << 3)
|
||||
#define ECS (1UL << 2)
|
||||
#define EC3S (1UL << 1)
|
||||
#define EC0S (1UL << 0)
|
||||
|
||||
/* SSR */
|
||||
#define PRQ (1UL << 31)
|
||||
#define PMA (1UL << 30)
|
||||
#define IIC_EA (1UL << 21)
|
||||
#define IIC_EW (1UL << 20)
|
||||
#define IIC_ER (1UL << 19)
|
||||
#define IIC_EL (1UL << 18)
|
||||
#define IIC_EF (1UL << 17)
|
||||
#define AF2_in (1UL << 10)
|
||||
#define AF2_out (1UL << 9)
|
||||
#define AF1_in (1UL << 8)
|
||||
#define AF1_out (1UL << 7)
|
||||
#define EC5S (1UL << 3)
|
||||
#define EC4S (1UL << 2)
|
||||
#define EC2S (1UL << 1)
|
||||
#define EC1S (1UL << 0)
|
||||
|
||||
/* PCI_BT_A */
|
||||
#define BurstA1_in (1UL << 26)
|
||||
#define ThreshA1_in (1UL << 24)
|
||||
#define BurstA1_out (1UL << 18)
|
||||
#define ThreshA1_out (1UL << 16)
|
||||
#define BurstA2_in (1UL << 10)
|
||||
#define ThreshA2_in (1UL << 8)
|
||||
#define BurstA2_out (1UL << 2)
|
||||
#define ThreshA2_out (1UL << 0)
|
||||
|
||||
/* MC1 */
|
||||
#define MRST_N (1UL << 15)
|
||||
#define EAP (1UL << 9)
|
||||
#define EI2C (1UL << 8)
|
||||
#define TR_E_A2_OUT (1UL << 3)
|
||||
#define TR_E_A2_IN (1UL << 2)
|
||||
#define TR_E_A1_OUT (1UL << 1)
|
||||
#define TR_E_A1_IN (1UL << 0)
|
||||
|
||||
/* MC2 */
|
||||
#define UPLD_IIC (1UL << 0)
|
||||
|
||||
/* ACON1 */
|
||||
#define AUDIO_MODE (1UL << 29)
|
||||
#define MAXLEVEL (1UL << 22)
|
||||
#define A1_SWAP (1UL << 21)
|
||||
#define A2_SWAP (1UL << 20)
|
||||
#define WS0_CTRL (1UL << 18)
|
||||
#define WS0_SYNC (1UL << 16)
|
||||
#define WS1_CTRL (1UL << 14)
|
||||
#define WS1_SYNC (1UL << 12)
|
||||
#define WS2_CTRL (1UL << 10)
|
||||
#define WS2_SYNC (1UL << 8)
|
||||
#define WS3_CTRL (1UL << 6)
|
||||
#define WS3_SYNC (1UL << 4)
|
||||
#define WS4_CTRL (1UL << 2)
|
||||
#define WS4_SYNC (1UL << 0)
|
||||
|
||||
/* ACON2 */
|
||||
#define A1_CLKSRC (1UL << 27)
|
||||
#define A2_CLKSRC (1UL << 22)
|
||||
#define INVERT_BCLK1 (1UL << 21)
|
||||
#define INVERT_BCLK2 (1UL << 20)
|
||||
#define BCLK1_OEN (1UL << 19)
|
||||
#define BCLK2_OEN (1UL << 18)
|
||||
|
||||
/* IICSTA */
|
||||
#define IICCC (1UL << 8)
|
||||
#define ABORT (1UL << 7)
|
||||
#define SPERR (1UL << 6)
|
||||
#define APERR (1UL << 5)
|
||||
#define DTERR (1UL << 4)
|
||||
#define DRERR (1UL << 3)
|
||||
#define AL (1UL << 2)
|
||||
#define ERR (1UL << 1)
|
||||
#define BUSY (1UL << 0)
|
||||
|
||||
/* IICTFR */
|
||||
#define BYTE2 (1UL << 24)
|
||||
#define BYTE1 (1UL << 16)
|
||||
#define BYTE0 (1UL << 8)
|
||||
#define ATRR2 (1UL << 6)
|
||||
#define ATRR1 (1UL << 4)
|
||||
#define ATRR0 (1UL << 2)
|
||||
#define ERR (1UL << 1)
|
||||
#define BUSY (1UL << 0)
|
||||
|
||||
#define START 3
|
||||
#define CONT 2
|
||||
#define STOP 1
|
||||
#define NOP 0
|
|
@ -157,8 +157,8 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
|
|||
|
||||
#if DEBUG_CALLS
|
||||
#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
|
||||
#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
|
||||
#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
|
||||
#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
|
||||
#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
|
||||
#else
|
||||
#define snd_azf3328_dbgcalls(format, args...)
|
||||
#define snd_azf3328_dbgcallenter()
|
||||
|
@ -1514,7 +1514,8 @@ snd_azf3328_free(struct snd_azf3328 *chip)
|
|||
/* well, at least we know how to disable the timer IRQ */
|
||||
snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00);
|
||||
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
synchronize_irq(chip->irq);
|
||||
__end_hw:
|
||||
snd_azf3328_free_joystick(chip);
|
||||
if (chip->irq >= 0)
|
||||
|
|
|
@ -435,22 +435,22 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
|
|||
static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
unsigned int intr_enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) | intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
intr_enable = inl(emu->port + INTE) | intrenb;
|
||||
outl(intr_enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
unsigned int intr_enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) & ~intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
intr_enable = inl(emu->port + INTE) & ~intrenb;
|
||||
outl(intr_enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -1114,6 +1114,8 @@ static int snd_ca0106_free(struct snd_ca0106 *chip)
|
|||
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
|
||||
*/
|
||||
}
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
// release the data
|
||||
#if 1
|
||||
if (chip->buffer.area)
|
||||
|
@ -1123,9 +1125,6 @@ static int snd_ca0106_free(struct snd_ca0106 *chip)
|
|||
// release the i/o port
|
||||
release_and_free_resource(chip->res_port);
|
||||
|
||||
// release the irq
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
|
|
|
@ -650,19 +650,55 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch
|
|||
|
||||
#define ADD_CTLS(emu, ctls) \
|
||||
do { \
|
||||
int i, err; \
|
||||
int i, _err; \
|
||||
for (i = 0; i < ARRAY_SIZE(ctls); i++) { \
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
|
||||
if (err < 0) \
|
||||
return err; \
|
||||
_err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \
|
||||
if (_err < 0) \
|
||||
return _err; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static __devinitdata
|
||||
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
|
||||
|
||||
static char *slave_vols[] __devinitdata = {
|
||||
"Analog Front Playback Volume",
|
||||
"Analog Rear Playback Volume",
|
||||
"Analog Center/LFE Playback Volume",
|
||||
"Analog Side Playback Volume",
|
||||
"IEC958 Front Playback Volume",
|
||||
"IEC958 Rear Playback Volume",
|
||||
"IEC958 Center/LFE Playback Volume",
|
||||
"IEC958 Unknown Playback Volume",
|
||||
"CAPTURE feedback Playback Volume",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *slave_sws[] __devinitdata = {
|
||||
"Analog Front Playback Switch",
|
||||
"Analog Rear Playback Switch",
|
||||
"Analog Center/LFE Playback Switch",
|
||||
"Analog Side Playback Switch",
|
||||
"IEC958 Playback Switch",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void __devinit add_slaves(struct snd_card *card,
|
||||
struct snd_kcontrol *master, char **list)
|
||||
{
|
||||
for (; *list; list++) {
|
||||
struct snd_kcontrol *slave = ctl_find(card, *list);
|
||||
if (slave)
|
||||
snd_ctl_add_slave(master, slave);
|
||||
}
|
||||
}
|
||||
|
||||
int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
|
||||
{
|
||||
int err;
|
||||
struct snd_card *card = emu->card;
|
||||
char **c;
|
||||
struct snd_kcontrol *vmaster;
|
||||
static char *ca0106_remove_ctls[] = {
|
||||
"Master Mono Playback Switch",
|
||||
"Master Mono Playback Volume",
|
||||
|
@ -719,6 +755,21 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
|
|||
}
|
||||
if (emu->details->spi_dac == 1)
|
||||
ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
|
||||
|
||||
/* Create virtual master controls */
|
||||
vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
|
||||
snd_ca0106_master_db_scale);
|
||||
if (!vmaster)
|
||||
return -ENOMEM;
|
||||
add_slaves(card, vmaster, slave_vols);
|
||||
|
||||
if (emu->details->spi_dac == 1) {
|
||||
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
|
||||
NULL);
|
||||
if (!vmaster)
|
||||
return -ENOMEM;
|
||||
add_slaves(card, vmaster, slave_sws);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2744,12 +2744,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
|
|||
}
|
||||
|
||||
for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
|
||||
struct snd_ctl_elem_id id;
|
||||
struct snd_ctl_elem_id elem_id;
|
||||
struct snd_kcontrol *ctl;
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(id.name, cm_saved_mixer[idx].name);
|
||||
if ((ctl = snd_ctl_find_id(cm->card, &id)) != NULL)
|
||||
memset(&elem_id, 0, sizeof(elem_id));
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, cm_saved_mixer[idx].name);
|
||||
ctl = snd_ctl_find_id(cm->card, &elem_id);
|
||||
if (ctl)
|
||||
cm->mixer_res_ctl[idx] = ctl;
|
||||
}
|
||||
|
||||
|
@ -2932,8 +2933,6 @@ static int snd_cmipci_free(struct cmipci *cm)
|
|||
/* reset mixer */
|
||||
snd_cmipci_mixer_write(cm, 0, 0);
|
||||
|
||||
synchronize_irq(cm->irq);
|
||||
|
||||
free_irq(cm->irq, cm);
|
||||
}
|
||||
|
||||
|
|
|
@ -2772,6 +2772,9 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
|
|||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
if (chip->active_ctrl)
|
||||
chip->active_ctrl(chip, -chip->amplifier);
|
||||
|
||||
for (idx = 0; idx < 5; idx++) {
|
||||
struct snd_cs46xx_region *region = &chip->region.idx[idx];
|
||||
if (region->remap_addr)
|
||||
|
@ -2779,9 +2782,6 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
|
|||
release_and_free_resource(region->resource);
|
||||
}
|
||||
|
||||
if (chip->active_ctrl)
|
||||
chip->active_ctrl(chip, -chip->amplifier);
|
||||
|
||||
#ifdef CONFIG_SND_CS46XX_NEW_DSP
|
||||
if (chip->dsp_spos_instance) {
|
||||
cs46xx_dsp_spos_destroy(chip);
|
||||
|
|
|
@ -1852,15 +1852,16 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
|
|||
static int snd_echo_free(struct echoaudio *chip)
|
||||
{
|
||||
DE_INIT(("Stop DSP...\n"));
|
||||
if (chip->comm_page) {
|
||||
if (chip->comm_page)
|
||||
rest_in_peace(chip);
|
||||
snd_dma_free_pages(&chip->commpage_dma_buf);
|
||||
}
|
||||
DE_INIT(("Stopped.\n"));
|
||||
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
if (chip->comm_page)
|
||||
snd_dma_free_pages(&chip->commpage_dma_buf);
|
||||
|
||||
if (chip->dsp_registers)
|
||||
iounmap(chip->dsp_registers);
|
||||
|
||||
|
|
|
@ -1249,11 +1249,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
|
|||
if (emu->port) { /* avoid access to already used hardware */
|
||||
snd_emu10k1_fx8010_tram_setup(emu, 0);
|
||||
snd_emu10k1_done(emu);
|
||||
/* remove reserved page */
|
||||
if (emu->reserved_page) {
|
||||
snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page);
|
||||
emu->reserved_page = NULL;
|
||||
}
|
||||
snd_emu10k1_free_efx(emu);
|
||||
}
|
||||
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
|
||||
|
@ -1262,6 +1257,14 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
|
|||
}
|
||||
if (emu->emu1010.firmware_thread)
|
||||
kthread_stop(emu->emu1010.firmware_thread);
|
||||
if (emu->irq >= 0)
|
||||
free_irq(emu->irq, emu);
|
||||
/* remove reserved page */
|
||||
if (emu->reserved_page) {
|
||||
snd_emu10k1_synth_free(emu,
|
||||
(struct snd_util_memblk *)emu->reserved_page);
|
||||
emu->reserved_page = NULL;
|
||||
}
|
||||
if (emu->memhdr)
|
||||
snd_util_memhdr_free(emu->memhdr);
|
||||
if (emu->silent_page.area)
|
||||
|
@ -1273,8 +1276,6 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
|
|||
#ifdef CONFIG_PM
|
||||
free_pm_buffer(emu);
|
||||
#endif
|
||||
if (emu->irq >= 0)
|
||||
free_irq(emu->irq, emu);
|
||||
if (emu->port)
|
||||
pci_release_regions(emu->pci);
|
||||
if (emu->card_capabilities->ca0151_chip) /* P16V */
|
||||
|
|
|
@ -327,22 +327,22 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
|
|||
static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
unsigned int intr_enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) | intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
intr_enable = inl(emu->port + INTE) | intrenb;
|
||||
outl(intr_enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
unsigned int intr_enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) & ~intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
intr_enable = inl(emu->port + INTE) & ~intrenb;
|
||||
outl(intr_enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -754,13 +754,13 @@ static int snd_emu10k1x_free(struct emu10k1x *chip)
|
|||
// disable audio
|
||||
outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
|
||||
|
||||
// release the i/o port
|
||||
release_and_free_resource(chip->res_port);
|
||||
|
||||
// release the irq
|
||||
/* release the irq */
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
||||
// release the i/o port
|
||||
release_and_free_resource(chip->res_port);
|
||||
|
||||
// release the DMA
|
||||
if (chip->dma_buffer.area) {
|
||||
snd_dma_free_pages(&chip->dma_buffer);
|
||||
|
@ -795,9 +795,9 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
|
|||
|
||||
// capture interrupt
|
||||
if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
|
||||
struct emu10k1x_voice *pvoice = &chip->capture_voice;
|
||||
if (pvoice->use)
|
||||
snd_emu10k1x_pcm_interrupt(chip, pvoice);
|
||||
struct emu10k1x_voice *cap_voice = &chip->capture_voice;
|
||||
if (cap_voice->use)
|
||||
snd_emu10k1x_pcm_interrupt(chip, cap_voice);
|
||||
else
|
||||
snd_emu10k1x_intr_disable(chip,
|
||||
INTE_CAP_0_LOOP |
|
||||
|
|
|
@ -412,7 +412,7 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
|
|||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
int value;
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
|
||||
|
|
|
@ -1635,20 +1635,20 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
|
|||
if (has_spdif > 0 ||
|
||||
(!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) {
|
||||
struct snd_kcontrol *kctl;
|
||||
int i, index = 0;
|
||||
int i, is_spdif = 0;
|
||||
|
||||
ensoniq->spdif_default = ensoniq->spdif_stream =
|
||||
SNDRV_PCM_DEFAULT_CON_SPDIF;
|
||||
outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS));
|
||||
|
||||
if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
|
||||
index++;
|
||||
is_spdif++;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) {
|
||||
kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq);
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
kctl->id.index = index;
|
||||
kctl->id.index = is_spdif;
|
||||
err = snd_ctl_add(card, kctl);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1910,7 +1910,8 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq)
|
|||
outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */
|
||||
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
|
||||
#endif
|
||||
synchronize_irq(ensoniq->irq);
|
||||
if (ensoniq->irq >= 0)
|
||||
synchronize_irq(ensoniq->irq);
|
||||
pci_set_power_state(ensoniq->pci, 3);
|
||||
__hw_end:
|
||||
#ifdef CHIP1370
|
||||
|
|
|
@ -1488,7 +1488,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
|
||||
outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
}
|
||||
|
@ -1578,10 +1577,8 @@ static int snd_es1938_free(struct es1938 *chip)
|
|||
|
||||
snd_es1938_free_gameport(chip);
|
||||
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
}
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
|
|
|
@ -1827,6 +1827,22 @@ snd_es1968_pcm(struct es1968 *chip, int device)
|
|||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* suppress jitter on some maestros when playing stereo
|
||||
*/
|
||||
static void snd_es1968_suppress_jitter(struct es1968 *chip, struct esschan *es)
|
||||
{
|
||||
unsigned int cp1;
|
||||
unsigned int cp2;
|
||||
unsigned int diff;
|
||||
|
||||
cp1 = __apu_get_register(chip, 0, 5);
|
||||
cp2 = __apu_get_register(chip, 1, 5);
|
||||
diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1);
|
||||
|
||||
if (diff > 1)
|
||||
__maestro_write(chip, IDR0_DATA_PORT, cp1);
|
||||
}
|
||||
|
||||
/*
|
||||
* update pointer
|
||||
|
@ -1948,8 +1964,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
|
|||
struct esschan *es;
|
||||
spin_lock(&chip->substream_lock);
|
||||
list_for_each_entry(es, &chip->substream_list, list) {
|
||||
if (es->running)
|
||||
if (es->running) {
|
||||
snd_es1968_update_pcm(chip, es);
|
||||
if (es->fmt & ESS_FMT_STEREO)
|
||||
snd_es1968_suppress_jitter(chip, es);
|
||||
}
|
||||
}
|
||||
spin_unlock(&chip->substream_lock);
|
||||
if (chip->in_measurement) {
|
||||
|
@ -1972,7 +1991,7 @@ snd_es1968_mixer(struct es1968 *chip)
|
|||
{
|
||||
struct snd_ac97_bus *pbus;
|
||||
struct snd_ac97_template ac97;
|
||||
struct snd_ctl_elem_id id;
|
||||
struct snd_ctl_elem_id elem_id;
|
||||
int err;
|
||||
static struct snd_ac97_bus_ops ops = {
|
||||
.write = snd_es1968_ac97_write,
|
||||
|
@ -1989,14 +2008,14 @@ snd_es1968_mixer(struct es1968 *chip)
|
|||
return err;
|
||||
|
||||
/* attach master switch / volumes for h/w volume control */
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(id.name, "Master Playback Switch");
|
||||
chip->master_switch = snd_ctl_find_id(chip->card, &id);
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(id.name, "Master Playback Volume");
|
||||
chip->master_volume = snd_ctl_find_id(chip->card, &id);
|
||||
memset(&elem_id, 0, sizeof(elem_id));
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, "Master Playback Switch");
|
||||
chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
|
||||
memset(&elem_id, 0, sizeof(elem_id));
|
||||
elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(elem_id.name, "Master Playback Volume");
|
||||
chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2456,7 +2475,8 @@ static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
|
|||
static int snd_es1968_free(struct es1968 *chip)
|
||||
{
|
||||
if (chip->io_port) {
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
synchronize_irq(chip->irq);
|
||||
outw(1, chip->io_port + 0x04); /* clear WP interrupts */
|
||||
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
|
||||
}
|
||||
|
|
|
@ -1285,7 +1285,6 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
|
|||
|
||||
static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
||||
{
|
||||
int id;
|
||||
unsigned short cmdw;
|
||||
|
||||
if (chip->tea575x_tuner & 0x0010)
|
||||
|
@ -1310,13 +1309,14 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
|
|||
} else {
|
||||
/* my card has the secondary codec */
|
||||
/* at address #3, so the loop is inverted */
|
||||
for (id = 3; id > 0; id--) {
|
||||
if (! wait_for_codec(chip, id, AC97_VENDOR_ID1,
|
||||
int i;
|
||||
for (i = 3; i > 0; i--) {
|
||||
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
|
||||
msecs_to_jiffies(50))) {
|
||||
cmdw = inw(FM801_REG(chip, AC97_DATA));
|
||||
if (cmdw != 0xffff && cmdw != 0) {
|
||||
chip->secondary = 1;
|
||||
chip->secondary_addr = id;
|
||||
chip->secondary_addr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
|
|||
# since snd-hda-intel is the only driver using hda-codec,
|
||||
# merge it into a single module although it was originally
|
||||
# designed to be individual modules
|
||||
snd-hda-intel-y += hda_codec.o vmaster.o
|
||||
snd-hda-intel-y += hda_codec.o
|
||||
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
|
||||
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <sound/initval.h>
|
||||
#include "hda_local.h"
|
||||
#include <sound/hda_hwdep.h>
|
||||
#include "hda_patch.h" /* codec presets */
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
/* define this option here to hide as static */
|
||||
|
@ -51,21 +52,50 @@ struct hda_vendor_id {
|
|||
|
||||
/* codec vendor labels */
|
||||
static struct hda_vendor_id hda_vendor_ids[] = {
|
||||
{ 0x10ec, "Realtek" },
|
||||
{ 0x1002, "ATI" },
|
||||
{ 0x1057, "Motorola" },
|
||||
{ 0x1095, "Silicon Image" },
|
||||
{ 0x10ec, "Realtek" },
|
||||
{ 0x1106, "VIA" },
|
||||
{ 0x111d, "IDT" },
|
||||
{ 0x11c1, "LSI" },
|
||||
{ 0x11d4, "Analog Devices" },
|
||||
{ 0x13f6, "C-Media" },
|
||||
{ 0x14f1, "Conexant" },
|
||||
{ 0x17e8, "Chrontel" },
|
||||
{ 0x1854, "LG" },
|
||||
{ 0x434d, "C-Media" },
|
||||
{ 0x8384, "SigmaTel" },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
/* codec presets */
|
||||
#include "hda_patch.h"
|
||||
|
||||
static const struct hda_codec_preset *hda_preset_tables[] = {
|
||||
#ifdef CONFIG_SND_HDA_CODEC_REALTEK
|
||||
snd_hda_preset_realtek,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
|
||||
snd_hda_preset_cmedia,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_ANALOG
|
||||
snd_hda_preset_analog,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
|
||||
snd_hda_preset_sigmatel,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_SI3054
|
||||
snd_hda_preset_si3054,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
snd_hda_preset_atihdmi,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
|
||||
snd_hda_preset_conexant,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_VIA
|
||||
snd_hda_preset_via,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static void hda_power_work(struct work_struct *work);
|
||||
|
@ -690,6 +720,19 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
|||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
|
||||
}
|
||||
|
||||
void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
if (!nid)
|
||||
return;
|
||||
|
||||
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
|
||||
#if 0 /* keep the format */
|
||||
msleep(1);
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* amp access functions
|
||||
*/
|
||||
|
@ -1037,16 +1080,24 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|||
}
|
||||
|
||||
/* find a mixer control element with the given name */
|
||||
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
||||
const char *name)
|
||||
static struct snd_kcontrol *
|
||||
_snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
||||
const char *name, int idx)
|
||||
{
|
||||
struct snd_ctl_elem_id id;
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
id.index = idx;
|
||||
strcpy(id.name, name);
|
||||
return snd_ctl_find_id(codec->bus->card, &id);
|
||||
}
|
||||
|
||||
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
|
||||
const char *name)
|
||||
{
|
||||
return _snd_hda_find_mixer_ctl(codec, name, 0);
|
||||
}
|
||||
|
||||
/* create a virtual master control and add slaves */
|
||||
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
|
||||
unsigned int *tlv, const char **slaves)
|
||||
|
@ -1481,6 +1532,8 @@ static struct snd_kcontrol_new dig_mixes[] = {
|
|||
{ } /* end */
|
||||
};
|
||||
|
||||
#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */
|
||||
|
||||
/**
|
||||
* snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
|
||||
* @codec: the HDA codec
|
||||
|
@ -1496,9 +1549,20 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
|
|||
int err;
|
||||
struct snd_kcontrol *kctl;
|
||||
struct snd_kcontrol_new *dig_mix;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
|
||||
if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
|
||||
idx))
|
||||
break;
|
||||
}
|
||||
if (idx >= SPDIF_MAX_IDX) {
|
||||
printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
|
||||
kctl = snd_ctl_new1(dig_mix, codec);
|
||||
kctl->id.index = idx;
|
||||
kctl->private_value = nid;
|
||||
err = snd_ctl_add(codec->bus->card, kctl);
|
||||
if (err < 0)
|
||||
|
@ -1511,6 +1575,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPDIF sharing with analog output
|
||||
*/
|
||||
static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
|
||||
ucontrol->value.integer.value[0] = mout->share_spdif;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
|
||||
mout->share_spdif = !!ucontrol->value.integer.value[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new spdif_share_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "IEC958 Default PCM Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = spdif_share_sw_get,
|
||||
.put = spdif_share_sw_put,
|
||||
};
|
||||
|
||||
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout)
|
||||
{
|
||||
if (!mout->dig_out_nid)
|
||||
return 0;
|
||||
/* ATTENTION: here mout is passed as private_data, instead of codec */
|
||||
return snd_ctl_add(codec->bus->card,
|
||||
snd_ctl_new1(&spdif_share_sw, mout));
|
||||
}
|
||||
|
||||
/*
|
||||
* SPDIF input
|
||||
*/
|
||||
|
@ -1595,7 +1696,17 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
|
|||
int err;
|
||||
struct snd_kcontrol *kctl;
|
||||
struct snd_kcontrol_new *dig_mix;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
|
||||
if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
|
||||
idx))
|
||||
break;
|
||||
}
|
||||
if (idx >= SPDIF_MAX_IDX) {
|
||||
printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
|
||||
kctl = snd_ctl_new1(dig_mix, codec);
|
||||
kctl->private_value = nid;
|
||||
|
@ -2106,7 +2217,7 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, hinfo->nid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2491,7 +2602,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
|
|||
mutex_lock(&codec->spdif_mutex);
|
||||
if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
|
||||
/* already opened as analog dup; reset it once */
|
||||
snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
|
||||
mout->dig_out_used = HDA_DIG_EXCLUSIVE;
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
return 0;
|
||||
|
@ -2526,9 +2637,36 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
|
|||
*/
|
||||
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout,
|
||||
struct snd_pcm_substream *substream)
|
||||
struct snd_pcm_substream *substream,
|
||||
struct hda_pcm_stream *hinfo)
|
||||
{
|
||||
substream->runtime->hw.channels_max = mout->max_channels;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
runtime->hw.channels_max = mout->max_channels;
|
||||
if (mout->dig_out_nid) {
|
||||
if (!mout->analog_rates) {
|
||||
mout->analog_rates = hinfo->rates;
|
||||
mout->analog_formats = hinfo->formats;
|
||||
mout->analog_maxbps = hinfo->maxbps;
|
||||
} else {
|
||||
runtime->hw.rates = mout->analog_rates;
|
||||
runtime->hw.formats = mout->analog_formats;
|
||||
hinfo->maxbps = mout->analog_maxbps;
|
||||
}
|
||||
if (!mout->spdif_rates) {
|
||||
snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
|
||||
&mout->spdif_rates,
|
||||
&mout->spdif_formats,
|
||||
&mout->spdif_maxbps);
|
||||
}
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
if (mout->share_spdif) {
|
||||
runtime->hw.rates &= mout->spdif_rates;
|
||||
runtime->hw.formats &= mout->spdif_formats;
|
||||
if (mout->spdif_maxbps < hinfo->maxbps)
|
||||
hinfo->maxbps = mout->spdif_maxbps;
|
||||
}
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
}
|
||||
return snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
||||
}
|
||||
|
@ -2548,7 +2686,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
|
|||
int i;
|
||||
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
|
||||
if (mout->dig_out_nid && mout->share_spdif &&
|
||||
mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
|
||||
if (chs == 2 &&
|
||||
snd_hda_is_supported_format(codec, mout->dig_out_nid,
|
||||
format) &&
|
||||
|
@ -2558,8 +2697,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
|
|||
stream_tag, format);
|
||||
} else {
|
||||
mout->dig_out_used = 0;
|
||||
snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
|
@ -2601,17 +2739,16 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
|
|||
int i;
|
||||
|
||||
for (i = 0; i < mout->num_dacs; i++)
|
||||
snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, nids[i]);
|
||||
if (mout->hp_nid)
|
||||
snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
|
||||
for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
|
||||
if (mout->extra_out_nid[i])
|
||||
snd_hda_codec_setup_stream(codec,
|
||||
mout->extra_out_nid[i],
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec,
|
||||
mout->extra_out_nid[i]);
|
||||
mutex_lock(&codec->spdif_mutex);
|
||||
if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
|
||||
snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid);
|
||||
mout->dig_out_used = 0;
|
||||
}
|
||||
mutex_unlock(&codec->spdif_mutex);
|
||||
|
@ -2790,6 +2927,30 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
/* FIX-UP:
|
||||
* If no line-out is defined but multiple HPs are found,
|
||||
* some of them might be the real line-outs.
|
||||
*/
|
||||
if (!cfg->line_outs && cfg->hp_outs > 1) {
|
||||
int i = 0;
|
||||
while (i < cfg->hp_outs) {
|
||||
/* The real HPs should have the sequence 0x0f */
|
||||
if ((sequences_hp[i] & 0x0f) == 0x0f) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
/* Move it to the line-out table */
|
||||
cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
|
||||
sequences_line_out[cfg->line_outs] = sequences_hp[i];
|
||||
cfg->line_outs++;
|
||||
cfg->hp_outs--;
|
||||
memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
|
||||
sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
|
||||
memmove(sequences_hp + i - 1, sequences_hp + i,
|
||||
sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
|
||||
}
|
||||
}
|
||||
|
||||
/* sort by sequence */
|
||||
sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
|
||||
cfg->line_outs);
|
||||
|
|
|
@ -590,11 +590,21 @@ struct hda_pcm_stream {
|
|||
struct hda_pcm_ops ops;
|
||||
};
|
||||
|
||||
/* PCM types */
|
||||
enum {
|
||||
HDA_PCM_TYPE_AUDIO,
|
||||
HDA_PCM_TYPE_SPDIF,
|
||||
HDA_PCM_TYPE_HDMI,
|
||||
HDA_PCM_TYPE_MODEM,
|
||||
HDA_PCM_NTYPES
|
||||
};
|
||||
|
||||
/* for PCM creation */
|
||||
struct hda_pcm {
|
||||
char *name;
|
||||
struct hda_pcm_stream stream[2];
|
||||
unsigned int is_modem; /* modem codec? */
|
||||
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
|
||||
int device; /* assigned device number */
|
||||
};
|
||||
|
||||
/* codec information */
|
||||
|
@ -712,6 +722,7 @@ int snd_hda_build_pcms(struct hda_bus *bus);
|
|||
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
|
||||
u32 stream_tag,
|
||||
int channel_id, int format);
|
||||
void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid);
|
||||
unsigned int snd_hda_calc_stream_format(unsigned int rate,
|
||||
unsigned int channels,
|
||||
unsigned int format,
|
||||
|
|
|
@ -1007,8 +1007,8 @@ static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
|
|||
{
|
||||
struct hda_gspec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
|
||||
snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, hinfo->nid);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -185,35 +186,28 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
|||
|
||||
/* max number of SDs */
|
||||
/* ICH, ATI and VIA have 4 playback and 4 capture */
|
||||
#define ICH6_CAPTURE_INDEX 0
|
||||
#define ICH6_NUM_CAPTURE 4
|
||||
#define ICH6_PLAYBACK_INDEX 4
|
||||
#define ICH6_NUM_PLAYBACK 4
|
||||
|
||||
/* ULI has 6 playback and 5 capture */
|
||||
#define ULI_CAPTURE_INDEX 0
|
||||
#define ULI_NUM_CAPTURE 5
|
||||
#define ULI_PLAYBACK_INDEX 5
|
||||
#define ULI_NUM_PLAYBACK 6
|
||||
|
||||
/* ATI HDMI has 1 playback and 0 capture */
|
||||
#define ATIHDMI_CAPTURE_INDEX 0
|
||||
#define ATIHDMI_NUM_CAPTURE 0
|
||||
#define ATIHDMI_PLAYBACK_INDEX 0
|
||||
#define ATIHDMI_NUM_PLAYBACK 1
|
||||
|
||||
/* this number is statically defined for simplicity */
|
||||
#define MAX_AZX_DEV 16
|
||||
|
||||
/* max number of fragments - we may use more if allocating more pages for BDL */
|
||||
#define BDL_SIZE PAGE_ALIGN(8192)
|
||||
#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16))
|
||||
#define BDL_SIZE 4096
|
||||
#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
|
||||
#define AZX_MAX_FRAG 32
|
||||
/* max buffer size - no h/w limit, you can increase as you like */
|
||||
#define AZX_MAX_BUF_SIZE (1024*1024*1024)
|
||||
/* max number of PCM devics per card */
|
||||
#define AZX_MAX_AUDIO_PCMS 6
|
||||
#define AZX_MAX_MODEM_PCMS 2
|
||||
#define AZX_MAX_PCMS (AZX_MAX_AUDIO_PCMS + AZX_MAX_MODEM_PCMS)
|
||||
#define AZX_MAX_PCMS 8
|
||||
|
||||
/* RIRB int mask: overrun[2], response[0] */
|
||||
#define RIRB_INT_RESPONSE 0x01
|
||||
|
@ -227,6 +221,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
|
|||
/* SD_CTL bits */
|
||||
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
|
||||
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
|
||||
#define SD_CTL_STRIPE (3 << 16) /* stripe control */
|
||||
#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
|
||||
#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
|
||||
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
|
||||
#define SD_CTL_STREAM_TAG_SHIFT 20
|
||||
|
||||
|
@ -284,12 +281,10 @@ enum {
|
|||
*/
|
||||
|
||||
struct azx_dev {
|
||||
u32 *bdl; /* virtual address of the BDL */
|
||||
dma_addr_t bdl_addr; /* physical address of the BDL */
|
||||
struct snd_dma_buffer bdl; /* BDL buffer */
|
||||
u32 *posbuf; /* position buffer pointer */
|
||||
|
||||
unsigned int bufsize; /* size of the play buffer in bytes */
|
||||
unsigned int fragsize; /* size of each period in bytes */
|
||||
unsigned int frags; /* number for period in the play buffer */
|
||||
unsigned int fifo_size; /* FIFO size */
|
||||
|
||||
|
@ -350,7 +345,6 @@ struct azx {
|
|||
struct azx_dev *azx_dev;
|
||||
|
||||
/* PCM */
|
||||
unsigned int pcm_devs;
|
||||
struct snd_pcm *pcm[AZX_MAX_PCMS];
|
||||
|
||||
/* HD codec */
|
||||
|
@ -361,8 +355,7 @@ struct azx {
|
|||
struct azx_rb corb;
|
||||
struct azx_rb rirb;
|
||||
|
||||
/* BDL, CORB/RIRB and position buffers */
|
||||
struct snd_dma_buffer bdl;
|
||||
/* CORB/RIRB and position buffers */
|
||||
struct snd_dma_buffer rb;
|
||||
struct snd_dma_buffer posbuf;
|
||||
|
||||
|
@ -546,8 +539,9 @@ static void azx_update_rirb(struct azx *chip)
|
|||
if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
|
||||
snd_hda_queue_unsol_event(chip->bus, res, res_ex);
|
||||
else if (chip->rirb.cmds) {
|
||||
chip->rirb.cmds--;
|
||||
chip->rirb.res = res;
|
||||
smp_wmb();
|
||||
chip->rirb.cmds--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -566,8 +560,10 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
|
|||
azx_update_rirb(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
if (!chip->rirb.cmds)
|
||||
if (!chip->rirb.cmds) {
|
||||
smp_rmb();
|
||||
return chip->rirb.res; /* the last value */
|
||||
}
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
if (codec->bus->needs_damn_long_delay)
|
||||
|
@ -965,30 +961,57 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
|
|||
/*
|
||||
* set up BDL entries
|
||||
*/
|
||||
static void azx_setup_periods(struct azx_dev *azx_dev)
|
||||
static int azx_setup_periods(struct snd_pcm_substream *substream,
|
||||
struct azx_dev *azx_dev)
|
||||
{
|
||||
u32 *bdl = azx_dev->bdl;
|
||||
dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
|
||||
int idx;
|
||||
struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
|
||||
u32 *bdl;
|
||||
int i, ofs, periods, period_bytes;
|
||||
|
||||
/* reset BDL address */
|
||||
azx_sd_writel(azx_dev, SD_BDLPL, 0);
|
||||
azx_sd_writel(azx_dev, SD_BDLPU, 0);
|
||||
|
||||
period_bytes = snd_pcm_lib_period_bytes(substream);
|
||||
periods = azx_dev->bufsize / period_bytes;
|
||||
|
||||
/* program the initial BDL entries */
|
||||
for (idx = 0; idx < azx_dev->frags; idx++) {
|
||||
unsigned int off = idx << 2; /* 4 dword step */
|
||||
dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
|
||||
/* program the address field of the BDL entry */
|
||||
bdl[off] = cpu_to_le32((u32)addr);
|
||||
bdl[off+1] = cpu_to_le32(upper_32bit(addr));
|
||||
|
||||
/* program the size field of the BDL entry */
|
||||
bdl[off+2] = cpu_to_le32(azx_dev->fragsize);
|
||||
|
||||
/* program the IOC to enable interrupt when buffer completes */
|
||||
bdl[off+3] = cpu_to_le32(0x01);
|
||||
bdl = (u32 *)azx_dev->bdl.area;
|
||||
ofs = 0;
|
||||
azx_dev->frags = 0;
|
||||
for (i = 0; i < periods; i++) {
|
||||
int size, rest;
|
||||
if (i >= AZX_MAX_BDL_ENTRIES) {
|
||||
snd_printk(KERN_ERR "Too many BDL entries: "
|
||||
"buffer=%d, period=%d\n",
|
||||
azx_dev->bufsize, period_bytes);
|
||||
/* reset */
|
||||
azx_sd_writel(azx_dev, SD_BDLPL, 0);
|
||||
azx_sd_writel(azx_dev, SD_BDLPU, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
rest = period_bytes;
|
||||
do {
|
||||
dma_addr_t addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs);
|
||||
/* program the address field of the BDL entry */
|
||||
bdl[0] = cpu_to_le32((u32)addr);
|
||||
bdl[1] = cpu_to_le32(upper_32bit(addr));
|
||||
/* program the size field of the BDL entry */
|
||||
size = PAGE_SIZE - (ofs % PAGE_SIZE);
|
||||
if (rest < size)
|
||||
size = rest;
|
||||
bdl[2] = cpu_to_le32(size);
|
||||
/* program the IOC to enable interrupt
|
||||
* only when the whole fragment is processed
|
||||
*/
|
||||
rest -= size;
|
||||
bdl[3] = rest ? 0 : cpu_to_le32(0x01);
|
||||
bdl += 4;
|
||||
azx_dev->frags++;
|
||||
ofs += size;
|
||||
} while (rest > 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1037,14 +1060,17 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
|
|||
|
||||
/* program the BDL address */
|
||||
/* lower BDL address */
|
||||
azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);
|
||||
azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
|
||||
/* upper BDL address */
|
||||
azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
|
||||
azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl.addr));
|
||||
|
||||
/* enable the position buffer */
|
||||
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
|
||||
azx_writel(chip, DPLBASE,
|
||||
(u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE);
|
||||
if (chip->position_fix == POS_FIX_POSBUF ||
|
||||
chip->position_fix == POS_FIX_AUTO) {
|
||||
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
|
||||
azx_writel(chip, DPLBASE,
|
||||
(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
|
||||
}
|
||||
|
||||
/* set the interrupt enable bits in the descriptor control register */
|
||||
azx_sd_writel(azx_dev, SD_CTL,
|
||||
|
@ -1157,7 +1183,8 @@ static struct snd_pcm_hardware azx_pcm_hw = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
/* No full-resume yet implemented */
|
||||
/* SNDRV_PCM_INFO_RESUME |*/
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START),
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
|
@ -1219,6 +1246,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
|
|||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||
|
||||
runtime->private_data = azx_dev;
|
||||
snd_pcm_set_sync(substream);
|
||||
mutex_unlock(&chip->open_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1275,8 +1303,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
|
||||
azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
|
||||
azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
|
||||
azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
|
||||
runtime->channels,
|
||||
runtime->format,
|
||||
|
@ -1288,10 +1314,10 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, "
|
||||
"format=0x%x\n",
|
||||
azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
|
||||
azx_setup_periods(azx_dev);
|
||||
snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
|
||||
azx_dev->bufsize, azx_dev->format_val);
|
||||
if (azx_setup_periods(substream, azx_dev) < 0)
|
||||
return -EINVAL;
|
||||
azx_setup_controller(chip, azx_dev);
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
|
||||
|
@ -1305,37 +1331,94 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
|
||||
struct azx_dev *azx_dev = get_azx_dev(substream);
|
||||
struct azx *chip = apcm->chip;
|
||||
int err = 0;
|
||||
struct azx_dev *azx_dev;
|
||||
struct snd_pcm_substream *s;
|
||||
int start, nsync = 0, sbits = 0;
|
||||
int nwait, timeout;
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
azx_stream_start(chip, azx_dev);
|
||||
azx_dev->running = 1;
|
||||
start = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
azx_stream_stop(chip, azx_dev);
|
||||
azx_dev->running = 0;
|
||||
start = 0;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (s->pcm->card != substream->pcm->card)
|
||||
continue;
|
||||
azx_dev = get_azx_dev(s);
|
||||
sbits |= 1 << azx_dev->index;
|
||||
nsync++;
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
}
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
if (nsync > 1) {
|
||||
/* first, set SYNC bits of corresponding streams */
|
||||
azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
|
||||
}
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (s->pcm->card != substream->pcm->card)
|
||||
continue;
|
||||
azx_dev = get_azx_dev(s);
|
||||
if (start)
|
||||
azx_stream_start(chip, azx_dev);
|
||||
else
|
||||
azx_stream_stop(chip, azx_dev);
|
||||
azx_dev->running = start;
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||
|
||||
cmd == SNDRV_PCM_TRIGGER_SUSPEND ||
|
||||
cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
int timeout = 5000;
|
||||
while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) &&
|
||||
--timeout)
|
||||
;
|
||||
if (start) {
|
||||
if (nsync == 1)
|
||||
return 0;
|
||||
/* wait until all FIFOs get ready */
|
||||
for (timeout = 5000; timeout; timeout--) {
|
||||
nwait = 0;
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (s->pcm->card != substream->pcm->card)
|
||||
continue;
|
||||
azx_dev = get_azx_dev(s);
|
||||
if (!(azx_sd_readb(azx_dev, SD_STS) &
|
||||
SD_STS_FIFO_READY))
|
||||
nwait++;
|
||||
}
|
||||
if (!nwait)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
} else {
|
||||
/* wait until all RUN bits are cleared */
|
||||
for (timeout = 5000; timeout; timeout--) {
|
||||
nwait = 0;
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (s->pcm->card != substream->pcm->card)
|
||||
continue;
|
||||
azx_dev = get_azx_dev(s);
|
||||
if (azx_sd_readb(azx_dev, SD_CTL) &
|
||||
SD_CTL_DMA_START)
|
||||
nwait++;
|
||||
}
|
||||
if (!nwait)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
return err;
|
||||
if (nsync > 1) {
|
||||
spin_lock(&chip->reg_lock);
|
||||
/* reset SYNC bits */
|
||||
azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
|
||||
spin_unlock(&chip->reg_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
|
@ -1378,6 +1461,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
|
|||
.prepare = azx_pcm_prepare,
|
||||
.trigger = azx_pcm_trigger,
|
||||
.pointer = azx_pcm_pointer,
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
static void azx_pcm_free(struct snd_pcm *pcm)
|
||||
|
@ -1386,7 +1470,7 @@ static void azx_pcm_free(struct snd_pcm *pcm)
|
|||
}
|
||||
|
||||
static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
|
||||
struct hda_pcm *cpcm, int pcm_dev)
|
||||
struct hda_pcm *cpcm)
|
||||
{
|
||||
int err;
|
||||
struct snd_pcm *pcm;
|
||||
|
@ -1400,7 +1484,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
|
|||
|
||||
snd_assert(cpcm->name, return -EINVAL);
|
||||
|
||||
err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
|
||||
err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
|
||||
cpcm->stream[0].substreams,
|
||||
cpcm->stream[1].substreams,
|
||||
&pcm);
|
||||
|
@ -1420,62 +1504,70 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
|
|||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
|
||||
if (cpcm->stream[1].substreams)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
1024 * 64, 1024 * 1024);
|
||||
chip->pcm[pcm_dev] = pcm;
|
||||
if (chip->pcm_devs < pcm_dev + 1)
|
||||
chip->pcm_devs = pcm_dev + 1;
|
||||
|
||||
chip->pcm[cpcm->device] = pcm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit azx_pcm_create(struct azx *chip)
|
||||
{
|
||||
static const char *dev_name[HDA_PCM_NTYPES] = {
|
||||
"Audio", "SPDIF", "HDMI", "Modem"
|
||||
};
|
||||
/* starting device index for each PCM type */
|
||||
static int dev_idx[HDA_PCM_NTYPES] = {
|
||||
[HDA_PCM_TYPE_AUDIO] = 0,
|
||||
[HDA_PCM_TYPE_SPDIF] = 1,
|
||||
[HDA_PCM_TYPE_HDMI] = 3,
|
||||
[HDA_PCM_TYPE_MODEM] = 6
|
||||
};
|
||||
/* normal audio device indices; not linear to keep compatibility */
|
||||
static int audio_idx[4] = { 0, 2, 4, 5 };
|
||||
struct hda_codec *codec;
|
||||
int c, err;
|
||||
int pcm_dev;
|
||||
int num_devs[HDA_PCM_NTYPES];
|
||||
|
||||
err = snd_hda_build_pcms(chip->bus);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* create audio PCMs */
|
||||
pcm_dev = 0;
|
||||
memset(num_devs, 0, sizeof(num_devs));
|
||||
list_for_each_entry(codec, &chip->bus->codec_list, list) {
|
||||
for (c = 0; c < codec->num_pcms; c++) {
|
||||
if (codec->pcm_info[c].is_modem)
|
||||
continue; /* create later */
|
||||
if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {
|
||||
snd_printk(KERN_ERR SFX
|
||||
"Too many audio PCMs\n");
|
||||
return -EINVAL;
|
||||
struct hda_pcm *cpcm = &codec->pcm_info[c];
|
||||
int type = cpcm->pcm_type;
|
||||
switch (type) {
|
||||
case HDA_PCM_TYPE_AUDIO:
|
||||
if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
|
||||
snd_printk(KERN_WARNING
|
||||
"Too many audio devices\n");
|
||||
continue;
|
||||
}
|
||||
cpcm->device = audio_idx[num_devs[type]];
|
||||
break;
|
||||
case HDA_PCM_TYPE_SPDIF:
|
||||
case HDA_PCM_TYPE_HDMI:
|
||||
case HDA_PCM_TYPE_MODEM:
|
||||
if (num_devs[type]) {
|
||||
snd_printk(KERN_WARNING
|
||||
"%s already defined\n",
|
||||
dev_name[type]);
|
||||
continue;
|
||||
}
|
||||
cpcm->device = dev_idx[type];
|
||||
break;
|
||||
default:
|
||||
snd_printk(KERN_WARNING
|
||||
"Invalid PCM type %d\n", type);
|
||||
continue;
|
||||
}
|
||||
err = create_codec_pcm(chip, codec,
|
||||
&codec->pcm_info[c], pcm_dev);
|
||||
num_devs[type]++;
|
||||
err = create_codec_pcm(chip, codec, cpcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm_dev++;
|
||||
}
|
||||
}
|
||||
|
||||
/* create modem PCMs */
|
||||
pcm_dev = AZX_MAX_AUDIO_PCMS;
|
||||
list_for_each_entry(codec, &chip->bus->codec_list, list) {
|
||||
for (c = 0; c < codec->num_pcms; c++) {
|
||||
if (!codec->pcm_info[c].is_modem)
|
||||
continue; /* already created */
|
||||
if (pcm_dev >= AZX_MAX_PCMS) {
|
||||
snd_printk(KERN_ERR SFX
|
||||
"Too many modem PCMs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = create_codec_pcm(chip, codec,
|
||||
&codec->pcm_info[c], pcm_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM;
|
||||
pcm_dev++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -1502,10 +1594,7 @@ static int __devinit azx_init_stream(struct azx *chip)
|
|||
* and initialize
|
||||
*/
|
||||
for (i = 0; i < chip->num_streams; i++) {
|
||||
unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
|
||||
struct azx_dev *azx_dev = &chip->azx_dev[i];
|
||||
azx_dev->bdl = (u32 *)(chip->bdl.area + off);
|
||||
azx_dev->bdl_addr = chip->bdl.addr + off;
|
||||
azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
|
||||
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
|
||||
azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
|
||||
|
@ -1587,13 +1676,12 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
int i;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
for (i = 0; i < chip->pcm_devs; i++)
|
||||
for (i = 0; i < AZX_MAX_PCMS; i++)
|
||||
snd_pcm_suspend_all(chip->pcm[i]);
|
||||
if (chip->initialized)
|
||||
snd_hda_suspend(chip->bus, state);
|
||||
azx_stop_chip(chip);
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
}
|
||||
|
@ -1641,24 +1729,26 @@ static int azx_resume(struct pci_dev *pci)
|
|||
*/
|
||||
static int azx_free(struct azx *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (chip->initialized) {
|
||||
int i;
|
||||
for (i = 0; i < chip->num_streams; i++)
|
||||
azx_stream_stop(chip, &chip->azx_dev[i]);
|
||||
azx_stop_chip(chip);
|
||||
}
|
||||
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, (void*)chip);
|
||||
}
|
||||
if (chip->msi)
|
||||
pci_disable_msi(chip->pci);
|
||||
if (chip->remap_addr)
|
||||
iounmap(chip->remap_addr);
|
||||
|
||||
if (chip->bdl.area)
|
||||
snd_dma_free_pages(&chip->bdl);
|
||||
if (chip->azx_dev) {
|
||||
for (i = 0; i < chip->num_streams; i++)
|
||||
if (chip->azx_dev[i].bdl.area)
|
||||
snd_dma_free_pages(&chip->azx_dev[i].bdl);
|
||||
}
|
||||
if (chip->rb.area)
|
||||
snd_dma_free_pages(&chip->rb);
|
||||
if (chip->posbuf.area)
|
||||
|
@ -1682,6 +1772,7 @@ static int azx_dev_free(struct snd_device *device)
|
|||
static struct snd_pci_quirk position_fix_list[] __devinitdata = {
|
||||
SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE),
|
||||
SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE),
|
||||
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_NONE),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -1740,7 +1831,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
struct azx **rchip)
|
||||
{
|
||||
struct azx *chip;
|
||||
int err;
|
||||
int i, err;
|
||||
unsigned short gcap;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = azx_dev_free,
|
||||
|
@ -1812,38 +1903,35 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
gcap = azx_readw(chip, GCAP);
|
||||
snd_printdd("chipset global capabilities = 0x%x\n", gcap);
|
||||
|
||||
if (gcap) {
|
||||
/* read number of streams from GCAP register instead of using
|
||||
* hardcoded value
|
||||
*/
|
||||
chip->playback_streams = (gcap & (0xF << 12)) >> 12;
|
||||
chip->capture_streams = (gcap & (0xF << 8)) >> 8;
|
||||
chip->playback_index_offset = chip->capture_streams;
|
||||
chip->capture_index_offset = 0;
|
||||
} else {
|
||||
/* allow 64bit DMA address if supported by H/W */
|
||||
if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_64BIT_MASK))
|
||||
pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK);
|
||||
|
||||
/* read number of streams from GCAP register instead of using
|
||||
* hardcoded value
|
||||
*/
|
||||
chip->capture_streams = (gcap >> 8) & 0x0f;
|
||||
chip->playback_streams = (gcap >> 12) & 0x0f;
|
||||
if (!chip->playback_streams && !chip->capture_streams) {
|
||||
/* gcap didn't give any info, switching to old method */
|
||||
|
||||
switch (chip->driver_type) {
|
||||
case AZX_DRIVER_ULI:
|
||||
chip->playback_streams = ULI_NUM_PLAYBACK;
|
||||
chip->capture_streams = ULI_NUM_CAPTURE;
|
||||
chip->playback_index_offset = ULI_PLAYBACK_INDEX;
|
||||
chip->capture_index_offset = ULI_CAPTURE_INDEX;
|
||||
break;
|
||||
case AZX_DRIVER_ATIHDMI:
|
||||
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
|
||||
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
|
||||
chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
|
||||
chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
|
||||
break;
|
||||
default:
|
||||
chip->playback_streams = ICH6_NUM_PLAYBACK;
|
||||
chip->capture_streams = ICH6_NUM_CAPTURE;
|
||||
chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
|
||||
chip->capture_index_offset = ICH6_CAPTURE_INDEX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chip->capture_index_offset = 0;
|
||||
chip->playback_index_offset = chip->capture_streams;
|
||||
chip->num_streams = chip->playback_streams + chip->capture_streams;
|
||||
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
|
||||
GFP_KERNEL);
|
||||
|
@ -1852,13 +1940,15 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
|
|||
goto errout;
|
||||
}
|
||||
|
||||
/* allocate memory for the BDL for each stream */
|
||||
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
BDL_SIZE, &chip->bdl);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
|
||||
goto errout;
|
||||
for (i = 0; i < chip->num_streams; i++) {
|
||||
/* allocate memory for the BDL for each stream */
|
||||
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
BDL_SIZE, &chip->azx_dev[i].bdl);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
/* allocate memory for the position buffer */
|
||||
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
|
||||
|
@ -1994,48 +2084,63 @@ static void __devexit azx_remove(struct pci_dev *pci)
|
|||
|
||||
/* PCI IDs */
|
||||
static struct pci_device_id azx_ids[] = {
|
||||
{ 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
|
||||
{ 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
|
||||
{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
|
||||
{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
|
||||
{ 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
|
||||
{ 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
|
||||
{ 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
|
||||
{ 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
|
||||
{ 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
|
||||
{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
|
||||
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
|
||||
{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
|
||||
{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
|
||||
{ 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
|
||||
{ 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
|
||||
{ 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
|
||||
{ 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
|
||||
{ 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
|
||||
{ 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
|
||||
{ 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
|
||||
{ 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
|
||||
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
|
||||
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
|
||||
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
|
||||
{ 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP51 */
|
||||
{ 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP55 */
|
||||
{ 0x10de, 0x03e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
|
||||
{ 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP61 */
|
||||
{ 0x10de, 0x044a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
|
||||
{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
|
||||
{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
|
||||
{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
|
||||
{ 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
|
||||
{ 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
|
||||
{ 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
|
||||
{ 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
|
||||
{ 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
|
||||
{ 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
|
||||
{ 0x10de, 0x0ac0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
|
||||
{ 0x10de, 0x0ac1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
|
||||
{ 0x10de, 0x0ac2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
|
||||
{ 0x10de, 0x0ac3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP79 */
|
||||
/* ICH 6..10 */
|
||||
{ PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
|
||||
{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
|
||||
/* SCH */
|
||||
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
|
||||
/* ATI SB 450/600 */
|
||||
{ PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
|
||||
{ PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
|
||||
/* ATI HDMI */
|
||||
{ PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
{ PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
|
||||
/* VIA VT8251/VT8237A */
|
||||
{ PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
|
||||
/* SIS966 */
|
||||
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
|
||||
/* ULI M5461 */
|
||||
{ PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
|
||||
/* NVIDIA MCP */
|
||||
{ PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0bd4), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0bd5), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0bd6), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
|
|
@ -228,8 +228,18 @@ struct hda_multi_out {
|
|||
int max_channels; /* currently supported analog channels */
|
||||
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
|
||||
int no_share_stream; /* don't share a stream with multiple pins */
|
||||
int share_spdif; /* share SPDIF pin */
|
||||
/* PCM information for both analog and SPDIF DACs */
|
||||
unsigned int analog_rates;
|
||||
unsigned int analog_maxbps;
|
||||
u64 analog_formats;
|
||||
unsigned int spdif_rates;
|
||||
unsigned int spdif_maxbps;
|
||||
u64 spdif_formats;
|
||||
};
|
||||
|
||||
int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout);
|
||||
int snd_hda_multi_out_dig_open(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout);
|
||||
int snd_hda_multi_out_dig_close(struct hda_codec *codec,
|
||||
|
@ -241,7 +251,8 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
|
|||
struct snd_pcm_substream *substream);
|
||||
int snd_hda_multi_out_analog_open(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout,
|
||||
struct snd_pcm_substream *substream);
|
||||
struct snd_pcm_substream *substream,
|
||||
struct hda_pcm_stream *hinfo);
|
||||
int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
|
||||
struct hda_multi_out *mout,
|
||||
unsigned int stream_tag,
|
||||
|
@ -407,11 +418,4 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
|
|||
hda_nid_t nid);
|
||||
#endif /* CONFIG_SND_HDA_POWER_SAVE */
|
||||
|
||||
/*
|
||||
* virtual master control
|
||||
*/
|
||||
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
|
||||
const unsigned int *tlv);
|
||||
int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
|
||||
|
||||
#endif /* __SOUND_HDA_LOCAL_H */
|
||||
|
|
|
@ -18,31 +18,3 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[];
|
|||
extern struct hda_codec_preset snd_hda_preset_conexant[];
|
||||
/* VIA codecs */
|
||||
extern struct hda_codec_preset snd_hda_preset_via[];
|
||||
|
||||
static const struct hda_codec_preset *hda_preset_tables[] = {
|
||||
#ifdef CONFIG_SND_HDA_CODEC_REALTEK
|
||||
snd_hda_preset_realtek,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
|
||||
snd_hda_preset_cmedia,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_ANALOG
|
||||
snd_hda_preset_analog,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
|
||||
snd_hda_preset_sigmatel,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_SI3054
|
||||
snd_hda_preset_si3054,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
|
||||
snd_hda_preset_atihdmi,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
|
||||
snd_hda_preset_conexant,
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_CODEC_VIA
|
||||
snd_hda_preset_via,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
struct ad198x_spec {
|
||||
struct snd_kcontrol_new *mixers[5];
|
||||
|
@ -80,7 +81,6 @@ struct ad198x_spec {
|
|||
#endif
|
||||
/* for virtual master */
|
||||
hda_nid_t vmaster_nid;
|
||||
u32 vmaster_tlv[4];
|
||||
const char **slave_vols;
|
||||
const char **slave_sws;
|
||||
};
|
||||
|
@ -171,6 +171,11 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
|||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
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_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
||||
|
@ -180,10 +185,11 @@ static int ad198x_build_controls(struct hda_codec *codec)
|
|||
|
||||
/* if we have no master control, let's create it */
|
||||
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
|
||||
unsigned int vmaster_tlv[4];
|
||||
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
|
||||
HDA_OUTPUT, spec->vmaster_tlv);
|
||||
HDA_OUTPUT, vmaster_tlv);
|
||||
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
|
||||
spec->vmaster_tlv,
|
||||
vmaster_tlv,
|
||||
(spec->slave_vols ?
|
||||
spec->slave_vols : ad_slave_vols));
|
||||
if (err < 0)
|
||||
|
@ -217,7 +223,8 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -289,8 +296,7 @@ static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct ad198x_spec *spec = codec->spec;
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -359,6 +365,7 @@ static int ad198x_build_pcms(struct hda_codec *codec)
|
|||
info++;
|
||||
codec->num_pcms++;
|
||||
info->name = "AD198x Digital";
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
|
||||
if (spec->dig_in_nid) {
|
||||
|
@ -611,13 +618,19 @@ static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct hda_input_mux ad1986a_automic_capture_source = {
|
||||
.num_items = 2,
|
||||
.items = {
|
||||
{ "Mic", 0x0 },
|
||||
{ "Mix", 0x5 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
|
||||
HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
|
||||
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
|
||||
|
@ -641,6 +654,33 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
|
|||
{ } /* end */
|
||||
};
|
||||
|
||||
/* re-connect the mic boost input according to the jack sensing */
|
||||
static void ad1986a_automic(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
|
||||
/* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
|
||||
snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
|
||||
(present & AC_PINSENSE_PRESENCE) ? 0 : 2);
|
||||
}
|
||||
|
||||
#define AD1986A_MIC_EVENT 0x36
|
||||
|
||||
static void ad1986a_automic_unsol_event(struct hda_codec *codec,
|
||||
unsigned int res)
|
||||
{
|
||||
if ((res >> 26) != AD1986A_MIC_EVENT)
|
||||
return;
|
||||
ad1986a_automic(codec);
|
||||
}
|
||||
|
||||
static int ad1986a_automic_init(struct hda_codec *codec)
|
||||
{
|
||||
ad198x_init(codec);
|
||||
ad1986a_automic(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* laptop-automute - 2ch only */
|
||||
|
||||
static void ad1986a_update_hp(struct hda_codec *codec)
|
||||
|
@ -844,6 +884,15 @@ static struct hda_verb ad1986a_eapd_init_verbs[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb ad1986a_automic_verbs[] = {
|
||||
{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
/*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
|
||||
{0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
{0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
|
||||
{}
|
||||
};
|
||||
|
||||
/* Ultra initialization */
|
||||
static struct hda_verb ad1986a_ultra_init[] = {
|
||||
/* eapd initialization */
|
||||
|
@ -986,14 +1035,17 @@ static int patch_ad1986a(struct hda_codec *codec)
|
|||
break;
|
||||
case AD1986A_LAPTOP_EAPD:
|
||||
spec->mixers[0] = ad1986a_laptop_eapd_mixers;
|
||||
spec->num_init_verbs = 2;
|
||||
spec->num_init_verbs = 3;
|
||||
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
|
||||
spec->init_verbs[2] = ad1986a_automic_verbs;
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.num_dacs = 1;
|
||||
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
|
||||
if (!is_jack_available(codec, 0x25))
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
|
||||
spec->input_mux = &ad1986a_automic_capture_source;
|
||||
codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
|
||||
codec->patch_ops.init = ad1986a_automic_init;
|
||||
break;
|
||||
case AD1986A_LAPTOP_AUTOMUTE:
|
||||
spec->mixers[0] = ad1986a_laptop_automute_mixers;
|
||||
|
@ -1365,7 +1417,10 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
|||
|
||||
if (! ad198x_eapd_put(kcontrol, ucontrol))
|
||||
return 0;
|
||||
|
||||
/* change speaker pin appropriately */
|
||||
snd_hda_codec_write(codec, 0x05, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
spec->cur_eapd ? PIN_OUT : 0);
|
||||
/* toggle HP mute appropriately */
|
||||
snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE,
|
||||
|
@ -2087,6 +2142,10 @@ static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
|
|||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
|
||||
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/*
|
||||
* initialization verbs
|
||||
|
@ -2187,6 +2246,13 @@ static struct hda_verb ad1988_spdif_init_verbs[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
/* AD1989 has no ADC -> SPDIF route */
|
||||
static struct hda_verb ad1989_spdif_init_verbs[] = {
|
||||
/* SPDIF out pin */
|
||||
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* verbs for 3stack (+dig)
|
||||
*/
|
||||
|
@ -2894,10 +2960,19 @@ static int patch_ad1988(struct hda_codec *codec)
|
|||
spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
spec->mixers[spec->num_mixers++] = ad1988_spdif_out_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1988_spdif_init_verbs;
|
||||
if (codec->vendor_id >= 0x11d4989a) {
|
||||
spec->mixers[spec->num_mixers++] =
|
||||
ad1989_spdif_out_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] =
|
||||
ad1989_spdif_init_verbs;
|
||||
} else {
|
||||
spec->mixers[spec->num_mixers++] =
|
||||
ad1988_spdif_out_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] =
|
||||
ad1988_spdif_init_verbs;
|
||||
}
|
||||
}
|
||||
if (spec->dig_in_nid)
|
||||
if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
|
||||
spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
|
||||
|
||||
codec->patch_ops = ad198x_patch_ops;
|
||||
|
@ -3133,11 +3208,12 @@ static int patch_ad1884(struct hda_codec *codec)
|
|||
* Lenovo Thinkpad T61/X61
|
||||
*/
|
||||
static struct hda_input_mux ad1984_thinkpad_capture_source = {
|
||||
.num_items = 3,
|
||||
.num_items = 4,
|
||||
.items = {
|
||||
{ "Mic", 0x0 },
|
||||
{ "Internal Mic", 0x1 },
|
||||
{ "Mix", 0x3 },
|
||||
{ "Docking-Station", 0x4 },
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3268,8 +3344,7 @@ static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct hda_codec *codec,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3355,6 +3430,472 @@ static int patch_ad1984(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* AD1883 / AD1884A / AD1984A / AD1984B
|
||||
*
|
||||
* port-B (0x14) - front mic-in
|
||||
* port-E (0x1c) - rear mic-in
|
||||
* port-F (0x16) - CD / ext out
|
||||
* port-C (0x15) - rear line-in
|
||||
* port-D (0x12) - rear line-out
|
||||
* port-A (0x11) - front hp-out
|
||||
*
|
||||
* AD1984A = AD1884A + digital-mic
|
||||
* AD1883 = equivalent with AD1984A
|
||||
* AD1984B = AD1984A + extra SPDIF-out
|
||||
*
|
||||
* FIXME:
|
||||
* We share the single DAC for both HP and line-outs (see AD1884/1984).
|
||||
*/
|
||||
|
||||
static hda_nid_t ad1884a_dac_nids[1] = {
|
||||
0x03,
|
||||
};
|
||||
|
||||
#define ad1884a_adc_nids ad1884_adc_nids
|
||||
#define ad1884a_capsrc_nids ad1884_capsrc_nids
|
||||
|
||||
#define AD1884A_SPDIF_OUT 0x02
|
||||
|
||||
static struct hda_input_mux ad1884a_capture_source = {
|
||||
.num_items = 5,
|
||||
.items = {
|
||||
{ "Front Mic", 0x0 },
|
||||
{ "Mic", 0x4 },
|
||||
{ "Line", 0x1 },
|
||||
{ "CD", 0x2 },
|
||||
{ "Mix", 0x3 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1884a_base_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
/* The multiple "Capture Source" controls confuse alsamixer
|
||||
* So call somewhat different..
|
||||
*/
|
||||
/* .name = "Capture Source", */
|
||||
.name = "Input Source",
|
||||
.count = 2,
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
/* SPDIF controls */
|
||||
HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
|
||||
/* identical with ad1983 */
|
||||
.info = ad1983_spdif_route_info,
|
||||
.get = ad1983_spdif_route_get,
|
||||
.put = ad1983_spdif_route_put,
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/*
|
||||
* initialization verbs
|
||||
*/
|
||||
static struct hda_verb ad1884a_init_verbs[] = {
|
||||
/* DACs; unmute as default */
|
||||
{0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
||||
{0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
|
||||
/* Port-A (HP) mixer - route only from analog mixer */
|
||||
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
||||
/* Port-A pin */
|
||||
{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Port-D (Line-out) mixer - route only from analog mixer */
|
||||
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
||||
/* Port-D pin */
|
||||
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Mono-out mixer - route only from analog mixer */
|
||||
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
||||
/* Mono-out pin */
|
||||
{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Port-B (front mic) pin */
|
||||
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Port-C (rear line-in) pin */
|
||||
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Port-E (rear mic) pin */
|
||||
{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
|
||||
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
|
||||
/* Port-F (CD) pin */
|
||||
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* Analog mixer; mute as default */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
|
||||
/* Analog Mix output amp */
|
||||
{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* capture sources */
|
||||
{0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
{0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
|
||||
{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* SPDIF output amp */
|
||||
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static struct hda_amp_list ad1884a_loopbacks[] = {
|
||||
{ 0x20, HDA_INPUT, 0 }, /* Front Mic */
|
||||
{ 0x20, HDA_INPUT, 1 }, /* Mic */
|
||||
{ 0x20, HDA_INPUT, 2 }, /* CD */
|
||||
{ 0x20, HDA_INPUT, 4 }, /* Docking */
|
||||
{ } /* end */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Laptop model
|
||||
*
|
||||
* Port A: Headphone jack
|
||||
* Port B: MIC jack
|
||||
* Port C: Internal MIC
|
||||
* Port D: Dock Line Out (if enabled)
|
||||
* Port E: Dock Line In (if enabled)
|
||||
* Port F: Internal speakers
|
||||
*/
|
||||
|
||||
static struct hda_input_mux ad1884a_laptop_capture_source = {
|
||||
.num_items = 4,
|
||||
.items = {
|
||||
{ "Mic", 0x0 }, /* port-B */
|
||||
{ "Internal Mic", 0x1 }, /* port-C */
|
||||
{ "Dock Mic", 0x4 }, /* port-E */
|
||||
{ "Mix", 0x3 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
/* The multiple "Capture Source" controls confuse alsamixer
|
||||
* So call somewhat different..
|
||||
*/
|
||||
/* .name = "Capture Source", */
|
||||
.name = "Input Source",
|
||||
.count = 2,
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_input_mux ad1884a_mobile_capture_source = {
|
||||
.num_items = 2,
|
||||
.items = {
|
||||
{ "Mic", 0x1 }, /* port-C */
|
||||
{ "Mix", 0x3 },
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/* mute internal speaker if HP is plugged */
|
||||
static void ad1884a_hp_automute(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0,
|
||||
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
|
||||
snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
|
||||
present ? 0x00 : 0x02);
|
||||
}
|
||||
|
||||
#define AD1884A_HP_EVENT 0x37
|
||||
|
||||
/* unsolicited event for HP jack sensing */
|
||||
static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
if ((res >> 26) != AD1884A_HP_EVENT)
|
||||
return;
|
||||
ad1884a_hp_automute(codec);
|
||||
}
|
||||
|
||||
/* initialize jack-sensing, too */
|
||||
static int ad1884a_hp_init(struct hda_codec *codec)
|
||||
{
|
||||
ad198x_init(codec);
|
||||
ad1884a_hp_automute(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* additional verbs for laptop model */
|
||||
static struct hda_verb ad1884a_laptop_verbs[] = {
|
||||
/* Port-A (HP) pin - always unmuted */
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
/* Port-F (int speaker) mixer - route only from analog mixer */
|
||||
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
|
||||
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
|
||||
/* Port-F pin */
|
||||
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
|
||||
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
|
||||
/* analog mix */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
||||
/* unsolicited event for pin-sense */
|
||||
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/*
|
||||
* Thinkpad X300
|
||||
* 0x11 - HP
|
||||
* 0x12 - speaker
|
||||
* 0x14 - mic-in
|
||||
* 0x17 - built-in mic
|
||||
*/
|
||||
|
||||
static struct hda_verb ad1984a_thinkpad_verbs[] = {
|
||||
/* HP unmute */
|
||||
{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
|
||||
/* analog mix */
|
||||
{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
|
||||
/* turn on EAPD */
|
||||
{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
|
||||
/* unsolicited event for pin-sense */
|
||||
{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
|
||||
/* internal mic - dmic */
|
||||
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
|
||||
/* set magic COEFs for dmic */
|
||||
{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
|
||||
{0x01, AC_VERB_SET_PROC_COEF, 0x08},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
|
||||
HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
|
||||
HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = ad198x_mux_enum_info,
|
||||
.get = ad198x_mux_enum_get,
|
||||
.put = ad198x_mux_enum_put,
|
||||
},
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static struct hda_input_mux ad1984a_thinkpad_capture_source = {
|
||||
.num_items = 3,
|
||||
.items = {
|
||||
{ "Mic", 0x0 },
|
||||
{ "Internal Mic", 0x5 },
|
||||
{ "Mix", 0x3 },
|
||||
},
|
||||
};
|
||||
|
||||
/* mute internal speaker if HP is plugged */
|
||||
static void ad1984a_thinkpad_automute(struct hda_codec *codec)
|
||||
{
|
||||
unsigned int present;
|
||||
|
||||
present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
|
||||
& AC_PINSENSE_PRESENCE;
|
||||
snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
|
||||
HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
|
||||
}
|
||||
|
||||
/* unsolicited event for HP jack sensing */
|
||||
static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
|
||||
unsigned int res)
|
||||
{
|
||||
if ((res >> 26) != AD1884A_HP_EVENT)
|
||||
return;
|
||||
ad1984a_thinkpad_automute(codec);
|
||||
}
|
||||
|
||||
/* initialize jack-sensing, too */
|
||||
static int ad1984a_thinkpad_init(struct hda_codec *codec)
|
||||
{
|
||||
ad198x_init(codec);
|
||||
ad1984a_thinkpad_automute(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
enum {
|
||||
AD1884A_DESKTOP,
|
||||
AD1884A_LAPTOP,
|
||||
AD1884A_MOBILE,
|
||||
AD1884A_THINKPAD,
|
||||
AD1884A_MODELS
|
||||
};
|
||||
|
||||
static const char *ad1884a_models[AD1884A_MODELS] = {
|
||||
[AD1884A_DESKTOP] = "desktop",
|
||||
[AD1884A_LAPTOP] = "laptop",
|
||||
[AD1884A_MOBILE] = "mobile",
|
||||
[AD1884A_THINKPAD] = "thinkpad",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
|
||||
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
|
||||
{}
|
||||
};
|
||||
|
||||
static int patch_ad1884a(struct hda_codec *codec)
|
||||
{
|
||||
struct ad198x_spec *spec;
|
||||
int board_config;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&spec->amp_mutex);
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
|
||||
spec->multiout.dac_nids = ad1884a_dac_nids;
|
||||
spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
|
||||
spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
|
||||
spec->adc_nids = ad1884a_adc_nids;
|
||||
spec->capsrc_nids = ad1884a_capsrc_nids;
|
||||
spec->input_mux = &ad1884a_capture_source;
|
||||
spec->num_mixers = 1;
|
||||
spec->mixers[0] = ad1884a_base_mixers;
|
||||
spec->num_init_verbs = 1;
|
||||
spec->init_verbs[0] = ad1884a_init_verbs;
|
||||
spec->spdif_route = 0;
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
spec->loopback.amplist = ad1884a_loopbacks;
|
||||
#endif
|
||||
codec->patch_ops = ad198x_patch_ops;
|
||||
|
||||
/* override some parameters */
|
||||
board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
|
||||
ad1884a_models,
|
||||
ad1884a_cfg_tbl);
|
||||
switch (board_config) {
|
||||
case AD1884A_LAPTOP:
|
||||
spec->mixers[0] = ad1884a_laptop_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1884a_laptop_capture_source;
|
||||
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
|
||||
codec->patch_ops.init = ad1884a_hp_init;
|
||||
break;
|
||||
case AD1884A_MOBILE:
|
||||
spec->mixers[0] = ad1884a_mobile_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1884a_mobile_capture_source;
|
||||
codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
|
||||
codec->patch_ops.init = ad1884a_hp_init;
|
||||
break;
|
||||
case AD1884A_THINKPAD:
|
||||
spec->mixers[0] = ad1984a_thinkpad_mixers;
|
||||
spec->init_verbs[spec->num_init_verbs++] =
|
||||
ad1984a_thinkpad_verbs;
|
||||
spec->multiout.dig_out_nid = 0;
|
||||
spec->input_mux = &ad1984a_thinkpad_capture_source;
|
||||
codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
|
||||
codec->patch_ops.init = ad1984a_thinkpad_init;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AD1882
|
||||
*
|
||||
|
@ -3654,13 +4195,19 @@ static int patch_ad1882(struct hda_codec *codec)
|
|||
* patch entries
|
||||
*/
|
||||
struct hda_codec_preset snd_hda_preset_analog[] = {
|
||||
{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
|
||||
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
|
||||
{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
|
||||
{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
|
||||
{ .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
|
||||
{ .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
|
||||
{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
|
||||
{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
|
||||
{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
|
||||
{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
|
||||
{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
|
||||
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
struct atihdmi_spec {
|
||||
struct hda_multi_out multiout;
|
||||
|
@ -58,6 +59,10 @@ static int atihdmi_build_controls(struct hda_codec *codec)
|
|||
static int atihdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_sequence_write(codec, atihdmi_basic_init);
|
||||
/* SI codec requires to unmute the pin */
|
||||
if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_UNMUTE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -112,6 +117,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
|
|||
codec->pcm_info = info;
|
||||
|
||||
info->name = "ATI HDMI";
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
|
||||
|
||||
return 0;
|
||||
|
@ -158,5 +164,7 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
|
|||
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
#define NUM_PINS 11
|
||||
|
||||
|
||||
|
@ -329,6 +330,11 @@ static int cmi9880_build_controls(struct hda_codec *codec)
|
|||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
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_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
||||
|
@ -432,7 +438,8 @@ static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -506,7 +513,7 @@ static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
{
|
||||
struct cmi_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -571,6 +578,7 @@ static int cmi9880_build_pcms(struct hda_codec *codec)
|
|||
codec->num_pcms++;
|
||||
info++;
|
||||
info->name = "CMI9880 Digital";
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
|
||||
|
@ -603,6 +611,7 @@ static const char *cmi9880_models[CMI_MODELS] = {
|
|||
|
||||
static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
|
||||
SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
#define CXT_PIN_DIR_IN 0x00
|
||||
#define CXT_PIN_DIR_OUT 0x01
|
||||
|
@ -98,7 +99,8 @@ static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -172,8 +174,7 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -241,7 +242,7 @@ static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct conexant_spec *spec = codec->spec;
|
||||
snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
|
||||
spec->cur_adc = 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -284,6 +285,7 @@ static int conexant_build_pcms(struct hda_codec *codec)
|
|||
info++;
|
||||
codec->num_pcms++;
|
||||
info->name = "Conexant Digital";
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
|
||||
conexant_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
|
||||
|
@ -371,6 +373,11 @@ static int conexant_build_controls(struct hda_codec *codec)
|
|||
spec->multiout.dig_out_nid);
|
||||
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_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
|
||||
|
@ -511,6 +518,14 @@ static struct hda_input_mux cxt5045_capture_source_benq = {
|
|||
}
|
||||
};
|
||||
|
||||
static struct hda_input_mux cxt5045_capture_source_hp530 = {
|
||||
.num_items = 2,
|
||||
.items = {
|
||||
{ "ExtMic", 0x1 },
|
||||
{ "IntMic", 0x2 },
|
||||
}
|
||||
};
|
||||
|
||||
/* turn on/off EAPD (+ mute HP) as a master switch */
|
||||
static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
|
@ -639,6 +654,37 @@ static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = conexant_mux_enum_info,
|
||||
.get = conexant_mux_enum_get,
|
||||
.put = conexant_mux_enum_put
|
||||
},
|
||||
HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
|
||||
HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.info = cxt_eapd_info,
|
||||
.get = cxt_eapd_get,
|
||||
.put = cxt5045_hp_master_sw_put,
|
||||
.private_value = 0x10,
|
||||
},
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb cxt5045_init_verbs[] = {
|
||||
/* Line in, Mic */
|
||||
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
|
||||
|
@ -833,6 +879,7 @@ enum {
|
|||
CXT5045_LAPTOP_MICSENSE,
|
||||
CXT5045_LAPTOP_HPMICSENSE,
|
||||
CXT5045_BENQ,
|
||||
CXT5045_LAPTOP_HP530,
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
CXT5045_TEST,
|
||||
#endif
|
||||
|
@ -844,6 +891,7 @@ static const char *cxt5045_models[CXT5045_MODELS] = {
|
|||
[CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
|
||||
[CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
|
||||
[CXT5045_BENQ] = "benq",
|
||||
[CXT5045_LAPTOP_HP530] = "laptop-hp530",
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
[CXT5045_TEST] = "test",
|
||||
#endif
|
||||
|
@ -857,7 +905,7 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
|
||||
SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
|
||||
SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
|
||||
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
|
||||
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
|
||||
SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
|
||||
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
|
||||
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
|
||||
|
@ -941,6 +989,14 @@ static int patch_cxt5045(struct hda_codec *codec)
|
|||
spec->num_mixers = 2;
|
||||
codec->patch_ops.init = cxt5045_init;
|
||||
break;
|
||||
case CXT5045_LAPTOP_HP530:
|
||||
codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
|
||||
spec->input_mux = &cxt5045_capture_source_hp530;
|
||||
spec->num_init_verbs = 2;
|
||||
spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
|
||||
spec->mixers[0] = cxt5045_mixers_hp530;
|
||||
codec->patch_ops.init = cxt5045_init;
|
||||
break;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
case CXT5045_TEST:
|
||||
spec->input_mux = &cxt5045_test_capture_source;
|
||||
|
@ -1537,7 +1593,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
|
|||
new_adc = spec->adc_nids[spec->cur_adc_idx];
|
||||
if (spec->cur_adc && spec->cur_adc != new_adc) {
|
||||
/* stream is running, let's swap the current ADC */
|
||||
snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
|
||||
spec->cur_adc = new_adc;
|
||||
snd_hda_codec_setup_stream(codec, new_adc,
|
||||
spec->cur_adc_stream_tag, 0,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -28,7 +28,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
#include "hda_patch.h"
|
||||
|
||||
/* si3054 verbs */
|
||||
#define SI3054_VERB_READ_NODE 0x900
|
||||
|
@ -206,7 +206,7 @@ static int si3054_build_pcms(struct hda_codec *codec)
|
|||
info->name = "Si3054 Modem";
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
|
||||
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
|
||||
info->is_modem = 1;
|
||||
info->pcm_type = HDA_PCM_TYPE_MODEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <sound/asoundef.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
|
||||
#define NUM_CONTROL_ALLOC 32
|
||||
#define STAC_PWR_EVENT 0x20
|
||||
|
@ -39,6 +40,7 @@
|
|||
|
||||
enum {
|
||||
STAC_REF,
|
||||
STAC_9200_OQO,
|
||||
STAC_9200_DELL_D21,
|
||||
STAC_9200_DELL_D22,
|
||||
STAC_9200_DELL_D23,
|
||||
|
@ -50,6 +52,7 @@ enum {
|
|||
STAC_9200_DELL_M26,
|
||||
STAC_9200_DELL_M27,
|
||||
STAC_9200_GATEWAY,
|
||||
STAC_9200_PANASONIC,
|
||||
STAC_9200_MODELS
|
||||
};
|
||||
|
||||
|
@ -63,11 +66,14 @@ enum {
|
|||
|
||||
enum {
|
||||
STAC_92HD73XX_REF,
|
||||
STAC_DELL_M6,
|
||||
STAC_92HD73XX_MODELS
|
||||
};
|
||||
|
||||
enum {
|
||||
STAC_92HD71BXX_REF,
|
||||
STAC_DELL_M4_1,
|
||||
STAC_DELL_M4_2,
|
||||
STAC_92HD71BXX_MODELS
|
||||
};
|
||||
|
||||
|
@ -123,6 +129,7 @@ struct sigmatel_spec {
|
|||
unsigned int hp_detect: 1;
|
||||
|
||||
/* gpio lines */
|
||||
unsigned int eapd_mask;
|
||||
unsigned int gpio_mask;
|
||||
unsigned int gpio_dir;
|
||||
unsigned int gpio_data;
|
||||
|
@ -135,6 +142,7 @@ struct sigmatel_spec {
|
|||
/* power management */
|
||||
unsigned int num_pwrs;
|
||||
hda_nid_t *pwr_nids;
|
||||
hda_nid_t *dac_list;
|
||||
|
||||
/* playback */
|
||||
struct hda_input_mux *mono_mux;
|
||||
|
@ -173,6 +181,7 @@ struct sigmatel_spec {
|
|||
/* i/o switches */
|
||||
unsigned int io_switch[2];
|
||||
unsigned int clfe_swap;
|
||||
unsigned int hp_switch;
|
||||
unsigned int aloopback;
|
||||
|
||||
struct hda_pcm pcm_rec[2]; /* PCM information */
|
||||
|
@ -184,9 +193,6 @@ struct sigmatel_spec {
|
|||
struct hda_input_mux private_dimux;
|
||||
struct hda_input_mux private_imux;
|
||||
struct hda_input_mux private_mono_mux;
|
||||
|
||||
/* virtual master */
|
||||
unsigned int vmaster_tlv[4];
|
||||
};
|
||||
|
||||
static hda_nid_t stac9200_adc_nids[1] = {
|
||||
|
@ -244,7 +250,7 @@ static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
|
|||
0x1c,
|
||||
};
|
||||
|
||||
static hda_nid_t stac92hd71bxx_dac_nids[2] = {
|
||||
static hda_nid_t stac92hd71bxx_dac_nids[1] = {
|
||||
0x10, /*0x11, */
|
||||
};
|
||||
|
||||
|
@ -290,6 +296,10 @@ static hda_nid_t stac927x_mux_nids[3] = {
|
|||
0x15, 0x16, 0x17
|
||||
};
|
||||
|
||||
static hda_nid_t stac927x_dac_nids[6] = {
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0
|
||||
};
|
||||
|
||||
static hda_nid_t stac927x_dmux_nids[1] = {
|
||||
0x1b,
|
||||
};
|
||||
|
@ -331,10 +341,10 @@ static hda_nid_t stac922x_pin_nids[10] = {
|
|||
0x0f, 0x10, 0x11, 0x15, 0x1b,
|
||||
};
|
||||
|
||||
static hda_nid_t stac92hd73xx_pin_nids[12] = {
|
||||
static hda_nid_t stac92hd73xx_pin_nids[13] = {
|
||||
0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x10, 0x11, 0x12, 0x13,
|
||||
0x14, 0x22
|
||||
0x14, 0x1e, 0x22
|
||||
};
|
||||
|
||||
static hda_nid_t stac92hd71bxx_pin_nids[10] = {
|
||||
|
@ -527,6 +537,43 @@ static struct hda_verb stac92hd73xx_6ch_core_init[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb dell_eq_core_init[] = {
|
||||
/* set master volume to max value without distortion
|
||||
* and direct control */
|
||||
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
|
||||
/* setup audio connections */
|
||||
{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
|
||||
/* setup adcs to point to mixer */
|
||||
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
|
||||
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
|
||||
/* setup import muxs */
|
||||
{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb dell_m6_core_init[] = {
|
||||
/* set master volume and direct control */
|
||||
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
|
||||
/* setup audio connections */
|
||||
{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
|
||||
/* setup adcs to point to mixer */
|
||||
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
|
||||
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
|
||||
/* setup import muxs */
|
||||
{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
|
||||
{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct hda_verb stac92hd73xx_8ch_core_init[] = {
|
||||
/* set master volume and direct control */
|
||||
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
|
||||
|
@ -910,6 +957,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
|
||||
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_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
||||
|
@ -919,10 +971,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
|||
|
||||
/* if we have no master control, let's create it */
|
||||
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
|
||||
unsigned int vmaster_tlv[4];
|
||||
snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
|
||||
HDA_OUTPUT, spec->vmaster_tlv);
|
||||
HDA_OUTPUT, vmaster_tlv);
|
||||
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
|
||||
spec->vmaster_tlv, slave_vols);
|
||||
vmaster_tlv, slave_vols);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -1052,9 +1105,15 @@ static unsigned int dell9200_m27_pin_configs[8] = {
|
|||
0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
|
||||
};
|
||||
|
||||
static unsigned int oqo9200_pin_configs[8] = {
|
||||
0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
|
||||
0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
|
||||
};
|
||||
|
||||
|
||||
static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
|
||||
[STAC_REF] = ref9200_pin_configs,
|
||||
[STAC_9200_OQO] = oqo9200_pin_configs,
|
||||
[STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
|
||||
[STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
|
||||
[STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
|
||||
|
@ -1065,10 +1124,12 @@ static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
|
|||
[STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
|
||||
[STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
|
||||
[STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
|
||||
[STAC_9200_PANASONIC] = ref9200_pin_configs,
|
||||
};
|
||||
|
||||
static const char *stac9200_models[STAC_9200_MODELS] = {
|
||||
[STAC_REF] = "ref",
|
||||
[STAC_9200_OQO] = "oqo",
|
||||
[STAC_9200_DELL_D21] = "dell-d21",
|
||||
[STAC_9200_DELL_D22] = "dell-d22",
|
||||
[STAC_9200_DELL_D23] = "dell-d23",
|
||||
|
@ -1080,6 +1141,7 @@ static const char *stac9200_models[STAC_9200_MODELS] = {
|
|||
[STAC_9200_DELL_M26] = "dell-m26",
|
||||
[STAC_9200_DELL_M27] = "dell-m27",
|
||||
[STAC_9200_GATEWAY] = "gateway",
|
||||
[STAC_9200_PANASONIC] = "panasonic",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac9200_cfg_tbl[] = {
|
||||
|
@ -1146,13 +1208,15 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
|
|||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
|
||||
"unknown Dell", STAC_9200_DELL_M26),
|
||||
/* Panasonic */
|
||||
SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
|
||||
SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
|
||||
/* Gateway machines needs EAPD to be set on resume */
|
||||
SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_GATEWAY),
|
||||
SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*",
|
||||
STAC_9200_GATEWAY),
|
||||
SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707",
|
||||
STAC_9200_GATEWAY),
|
||||
/* OQO Mobile */
|
||||
SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -1202,24 +1266,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = {
|
|||
{} /* terminator */
|
||||
};
|
||||
|
||||
static unsigned int ref92hd73xx_pin_configs[12] = {
|
||||
static unsigned int ref92hd73xx_pin_configs[13] = {
|
||||
0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
|
||||
0x0181302e, 0x01014010, 0x01014020, 0x01014030,
|
||||
0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
|
||||
0x01452050,
|
||||
};
|
||||
|
||||
static unsigned int dell_m6_pin_configs[13] = {
|
||||
0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
|
||||
0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
|
||||
0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
|
||||
0x4f0000f0,
|
||||
};
|
||||
|
||||
static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
|
||||
[STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
|
||||
[STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
|
||||
[STAC_DELL_M6] = dell_m6_pin_configs,
|
||||
};
|
||||
|
||||
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
|
||||
[STAC_92HD73XX_REF] = "ref",
|
||||
[STAC_DELL_M6] = "dell-m6",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
|
||||
/* SigmaTel reference board */
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
|
||||
"DFI LanParty", STAC_92HD73XX_REF),
|
||||
"DFI LanParty", STAC_92HD73XX_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
|
||||
"unknown Dell", STAC_DELL_M6),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -1229,18 +1317,56 @@ static unsigned int ref92hd71bxx_pin_configs[10] = {
|
|||
0x90a000f0, 0x01452050,
|
||||
};
|
||||
|
||||
static unsigned int dell_m4_1_pin_configs[13] = {
|
||||
0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
|
||||
0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
|
||||
0x40f000f0, 0x4f0000f0,
|
||||
};
|
||||
|
||||
static unsigned int dell_m4_2_pin_configs[13] = {
|
||||
0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
|
||||
0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
|
||||
0x40f000f0, 0x044413b0,
|
||||
};
|
||||
|
||||
static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
|
||||
[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
|
||||
[STAC_DELL_M4_1] = dell_m4_1_pin_configs,
|
||||
[STAC_DELL_M4_2] = dell_m4_2_pin_configs,
|
||||
};
|
||||
|
||||
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
|
||||
[STAC_92HD71BXX_REF] = "ref",
|
||||
[STAC_DELL_M4_1] = "dell-m4-1",
|
||||
[STAC_DELL_M4_2] = "dell-m4-2",
|
||||
};
|
||||
|
||||
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
|
||||
/* SigmaTel reference board */
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
|
||||
"DFI LanParty", STAC_92HD71BXX_REF),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
|
||||
"unknown Dell", STAC_DELL_M4_1),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
|
||||
"unknown Dell", STAC_DELL_M4_2),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
|
||||
"unknown Dell", STAC_DELL_M4_2),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
|
||||
"unknown Dell", STAC_DELL_M4_2),
|
||||
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
|
||||
"unknown Dell", STAC_DELL_M4_2),
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
|
@ -1733,7 +1859,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -1807,7 +1934,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1889,6 +2016,7 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
|
|||
codec->num_pcms++;
|
||||
info++;
|
||||
info->name = "STAC92xx Digital";
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
|
||||
|
@ -1925,6 +2053,34 @@ static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int
|
|||
AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
|
||||
}
|
||||
|
||||
#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int stac92xx_hp_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;
|
||||
|
||||
ucontrol->value.integer.value[0] = spec->hp_switch;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
spec->hp_switch = ucontrol->value.integer.value[0];
|
||||
|
||||
/* check to be sure that the ports are upto date with
|
||||
* switch changes
|
||||
*/
|
||||
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
|
@ -1996,6 +2152,15 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
|
|||
return 1;
|
||||
}
|
||||
|
||||
#define STAC_CODEC_HP_SWITCH(xname) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.index = 0, \
|
||||
.info = stac92xx_hp_switch_info, \
|
||||
.get = stac92xx_hp_switch_get, \
|
||||
.put = stac92xx_hp_switch_put, \
|
||||
}
|
||||
|
||||
#define STAC_CODEC_IO_SWITCH(xname, xpval) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
|
@ -2020,6 +2185,7 @@ enum {
|
|||
STAC_CTL_WIDGET_VOL,
|
||||
STAC_CTL_WIDGET_MUTE,
|
||||
STAC_CTL_WIDGET_MONO_MUX,
|
||||
STAC_CTL_WIDGET_HP_SWITCH,
|
||||
STAC_CTL_WIDGET_IO_SWITCH,
|
||||
STAC_CTL_WIDGET_CLFE_SWITCH
|
||||
};
|
||||
|
@ -2028,6 +2194,7 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
|
|||
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
|
||||
HDA_CODEC_MUTE(NULL, 0, 0, 0),
|
||||
STAC_MONO_MUX,
|
||||
STAC_CODEC_HP_SWITCH(NULL),
|
||||
STAC_CODEC_IO_SWITCH(NULL, 0),
|
||||
STAC_CODEC_CLFE_SWITCH(NULL, 0),
|
||||
};
|
||||
|
@ -2222,6 +2389,29 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
|
||||
{
|
||||
if (!spec->multiout.hp_nid)
|
||||
spec->multiout.hp_nid = nid;
|
||||
else if (spec->multiout.num_dacs > 4) {
|
||||
printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
|
||||
return 1;
|
||||
} else {
|
||||
spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
|
||||
spec->multiout.num_dacs++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
|
||||
{
|
||||
if (is_in_dac_nids(spec, nid))
|
||||
return 1;
|
||||
if (spec->multiout.hp_nid == nid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add playback controls from the parsed DAC table */
|
||||
static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
||||
const struct auto_pin_cfg *cfg)
|
||||
|
@ -2236,7 +2426,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
unsigned int wid_caps, pincap;
|
||||
|
||||
|
||||
for (i = 0; i < cfg->line_outs; i++) {
|
||||
for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
|
||||
if (!spec->multiout.dac_nids[i])
|
||||
continue;
|
||||
|
||||
|
@ -2269,6 +2459,14 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
}
|
||||
}
|
||||
|
||||
if (cfg->hp_outs > 1) {
|
||||
err = stac92xx_add_control(spec,
|
||||
STAC_CTL_WIDGET_HP_SWITCH,
|
||||
"Headphone as Line Out Switch", 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spec->line_switch) {
|
||||
nid = cfg->input_pins[AUTO_PIN_LINE];
|
||||
pincap = snd_hda_param_read(codec, nid,
|
||||
|
@ -2284,10 +2482,11 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
|
||||
if (spec->mic_switch) {
|
||||
unsigned int def_conf;
|
||||
nid = cfg->input_pins[AUTO_PIN_MIC];
|
||||
unsigned int mic_pin = AUTO_PIN_MIC;
|
||||
again:
|
||||
nid = cfg->input_pins[mic_pin];
|
||||
def_conf = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CONFIG_DEFAULT, 0);
|
||||
|
||||
/* some laptops have an internal analog microphone
|
||||
* which can't be used as a output */
|
||||
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
|
||||
|
@ -2297,38 +2496,22 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
|
|||
err = stac92xx_add_control(spec,
|
||||
STAC_CTL_WIDGET_IO_SWITCH,
|
||||
"Mic as Output Switch", (nid << 8) | 1);
|
||||
nid = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
|
||||
if (!check_in_dac_nids(spec, nid))
|
||||
add_spec_dacs(spec, nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
} else if (mic_pin == AUTO_PIN_MIC) {
|
||||
mic_pin = AUTO_PIN_FRONT_MIC;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
|
||||
{
|
||||
if (is_in_dac_nids(spec, nid))
|
||||
return 1;
|
||||
if (spec->multiout.hp_nid == nid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
|
||||
{
|
||||
if (!spec->multiout.hp_nid)
|
||||
spec->multiout.hp_nid = nid;
|
||||
else if (spec->multiout.num_dacs > 4) {
|
||||
printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
|
||||
return 1;
|
||||
} else {
|
||||
spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
|
||||
spec->multiout.num_dacs++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add playback controls for Speaker and HP outputs */
|
||||
static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
|
||||
struct auto_pin_cfg *cfg)
|
||||
|
@ -2378,12 +2561,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
|
|||
return err;
|
||||
}
|
||||
if (spec->multiout.hp_nid) {
|
||||
const char *pfx;
|
||||
if (old_num_dacs == spec->multiout.num_dacs)
|
||||
pfx = "Master";
|
||||
else
|
||||
pfx = "Headphone";
|
||||
err = create_controls(spec, pfx, spec->multiout.hp_nid, 3);
|
||||
err = create_controls(spec, "Headphone",
|
||||
spec->multiout.hp_nid, 3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -2745,7 +2924,7 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
|
|||
*/
|
||||
for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
|
||||
hda_nid_t pin = spec->autocfg.speaker_pins[i];
|
||||
unsigned long wcaps = get_wcaps(codec, pin);
|
||||
unsigned int wcaps = get_wcaps(codec, pin);
|
||||
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
|
||||
if (wcaps == AC_WCAP_OUT_AMP)
|
||||
/* found a mono speaker with an amp, must be lfe */
|
||||
|
@ -2756,12 +2935,12 @@ static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
|
|||
if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
|
||||
for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
|
||||
hda_nid_t pin = spec->autocfg.line_out_pins[i];
|
||||
unsigned long cfg;
|
||||
cfg = snd_hda_codec_read(codec, pin, 0,
|
||||
unsigned int defcfg;
|
||||
defcfg = snd_hda_codec_read(codec, pin, 0,
|
||||
AC_VERB_GET_CONFIG_DEFAULT,
|
||||
0x00);
|
||||
if (get_defcfg_device(cfg) == AC_JACK_SPEAKER) {
|
||||
unsigned long wcaps = get_wcaps(codec, pin);
|
||||
if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
|
||||
unsigned int wcaps = get_wcaps(codec, pin);
|
||||
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
|
||||
if (wcaps == AC_WCAP_OUT_AMP)
|
||||
/* found a mono speaker with an amp,
|
||||
|
@ -2866,6 +3045,19 @@ static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
|
|||
return 0; /* nid is not a HP-Out */
|
||||
};
|
||||
|
||||
static void stac92xx_power_down(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
/* power down inactive DACs */
|
||||
hda_nid_t *dac;
|
||||
for (dac = spec->dac_list; *dac; dac++)
|
||||
if (!is_in_dac_nids(spec, *dac) &&
|
||||
spec->multiout.hp_nid != *dac)
|
||||
snd_hda_codec_write_cache(codec, *dac, 0,
|
||||
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
|
||||
}
|
||||
|
||||
static int stac92xx_init(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
@ -2909,16 +3101,21 @@ static int stac92xx_init(struct hda_codec *codec)
|
|||
? STAC_HP_EVENT : STAC_PWR_EVENT;
|
||||
int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
|
||||
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
|
||||
0, AC_VERB_GET_CONFIG_DEFAULT, 0);
|
||||
/* outputs are only ports capable of power management
|
||||
* any attempts on powering down a input port cause the
|
||||
* referenced VREF to act quirky.
|
||||
*/
|
||||
if (pinctl & AC_PINCTL_IN_EN)
|
||||
continue;
|
||||
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED)
|
||||
continue;
|
||||
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
|
||||
codec->patch_ops.unsol_event(codec, (event | i) << 26);
|
||||
}
|
||||
|
||||
if (spec->dac_list)
|
||||
stac92xx_power_down(codec);
|
||||
if (cfg->dig_out_pin)
|
||||
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
|
||||
AC_PINCTL_OUT_EN);
|
||||
|
@ -3014,6 +3211,7 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
|
|||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int nid = cfg->hp_pins[cfg->hp_outs - 1];
|
||||
int i, presence;
|
||||
|
||||
presence = 0;
|
||||
|
@ -3024,26 +3222,42 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
|
|||
for (i = 0; i < cfg->hp_outs; i++) {
|
||||
if (presence)
|
||||
break;
|
||||
if (spec->hp_switch && cfg->hp_pins[i] == nid)
|
||||
break;
|
||||
presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
|
||||
}
|
||||
|
||||
if (presence) {
|
||||
/* disable lineouts, enable hp */
|
||||
if (spec->hp_switch)
|
||||
stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN);
|
||||
for (i = 0; i < cfg->line_outs; i++)
|
||||
stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
|
||||
AC_PINCTL_OUT_EN);
|
||||
for (i = 0; i < cfg->speaker_outs; i++)
|
||||
stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
|
||||
AC_PINCTL_OUT_EN);
|
||||
if (spec->eapd_mask)
|
||||
stac_gpio_set(codec, spec->gpio_mask,
|
||||
spec->gpio_dir, spec->gpio_data &
|
||||
~spec->eapd_mask);
|
||||
} else {
|
||||
/* enable lineouts, disable hp */
|
||||
if (spec->hp_switch)
|
||||
stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
|
||||
for (i = 0; i < cfg->line_outs; i++)
|
||||
stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
|
||||
AC_PINCTL_OUT_EN);
|
||||
for (i = 0; i < cfg->speaker_outs; i++)
|
||||
stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
|
||||
AC_PINCTL_OUT_EN);
|
||||
if (spec->eapd_mask)
|
||||
stac_gpio_set(codec, spec->gpio_mask,
|
||||
spec->gpio_dir, spec->gpio_data |
|
||||
spec->eapd_mask);
|
||||
}
|
||||
if (!spec->hp_switch && cfg->hp_outs > 1 && presence)
|
||||
stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
|
||||
}
|
||||
|
||||
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
|
||||
|
@ -3091,6 +3305,9 @@ static int stac92xx_resume(struct hda_codec *codec)
|
|||
spec->gpio_dir, spec->gpio_data);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
/* power down inactive DACs */
|
||||
if (spec->dac_list)
|
||||
stac92xx_power_down(codec);
|
||||
/* invoke unsolicited event to reset the HP state */
|
||||
if (spec->hp_detect)
|
||||
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
|
||||
|
@ -3147,12 +3364,18 @@ static int patch_stac9200(struct hda_codec *codec)
|
|||
spec->num_adcs = 1;
|
||||
spec->num_pwrs = 0;
|
||||
|
||||
if (spec->board_config == STAC_9200_GATEWAY)
|
||||
if (spec->board_config == STAC_9200_GATEWAY ||
|
||||
spec->board_config == STAC_9200_OQO)
|
||||
spec->init = stac9200_eapd_init;
|
||||
else
|
||||
spec->init = stac9200_core_init;
|
||||
spec->mixer = stac9200_mixer;
|
||||
|
||||
if (spec->board_config == STAC_9200_PANASONIC) {
|
||||
spec->gpio_mask = spec->gpio_dir = 0x09;
|
||||
spec->gpio_data = 0x00;
|
||||
}
|
||||
|
||||
err = stac9200_parse_auto_config(codec);
|
||||
if (err < 0) {
|
||||
stac92xx_free(codec);
|
||||
|
@ -3293,6 +3516,7 @@ again:
|
|||
|
||||
switch (spec->multiout.num_dacs) {
|
||||
case 0x3: /* 6 Channel */
|
||||
spec->multiout.hp_nid = 0x17;
|
||||
spec->mixer = stac92hd73xx_6ch_mixer;
|
||||
spec->init = stac92hd73xx_6ch_core_init;
|
||||
break;
|
||||
|
@ -3318,13 +3542,42 @@ again:
|
|||
|
||||
spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
|
||||
spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
|
||||
spec->num_dmics = STAC92HD73XX_NUM_DMICS;
|
||||
spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
|
||||
spec->dinput_mux = &stac92hd73xx_dmux;
|
||||
/* GPIO0 High = Enable EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->gpio_data = 0x01;
|
||||
|
||||
switch (spec->board_config) {
|
||||
case STAC_DELL_M6:
|
||||
spec->init = dell_eq_core_init;
|
||||
switch (codec->subsystem_id) {
|
||||
case 0x1028025e: /* Analog Mics */
|
||||
case 0x1028025f:
|
||||
stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
|
||||
spec->num_dmics = 0;
|
||||
break;
|
||||
case 0x10280271: /* Digital Mics */
|
||||
case 0x10280272:
|
||||
spec->init = dell_m6_core_init;
|
||||
/* fall-through */
|
||||
case 0x10280254:
|
||||
case 0x10280255:
|
||||
stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
|
||||
spec->num_dmics = 1;
|
||||
break;
|
||||
case 0x10280256: /* Both */
|
||||
case 0x10280057:
|
||||
stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
|
||||
stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
|
||||
spec->num_dmics = 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
spec->num_dmics = STAC92HD73XX_NUM_DMICS;
|
||||
}
|
||||
|
||||
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
|
||||
spec->pwr_nids = stac92hd73xx_pwr_nids;
|
||||
|
||||
|
@ -3398,7 +3651,10 @@ again:
|
|||
spec->aloopback_shift = 0;
|
||||
|
||||
/* GPIO0 High = EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1;
|
||||
spec->gpio_mask = 0x01;
|
||||
spec->gpio_dir = 0x01;
|
||||
spec->gpio_mask = 0x01;
|
||||
spec->gpio_data = 0x01;
|
||||
|
||||
spec->mux_nids = stac92hd71bxx_mux_nids;
|
||||
spec->adc_nids = stac92hd71bxx_adc_nids;
|
||||
|
@ -3413,7 +3669,7 @@ again:
|
|||
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
|
||||
spec->pwr_nids = stac92hd71bxx_pwr_nids;
|
||||
|
||||
spec->multiout.num_dacs = 2;
|
||||
spec->multiout.num_dacs = 1;
|
||||
spec->multiout.hp_nid = 0x11;
|
||||
spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
|
||||
|
||||
|
@ -3577,13 +3833,14 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
|
||||
spec->mux_nids = stac927x_mux_nids;
|
||||
spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
|
||||
spec->dac_list = stac927x_dac_nids;
|
||||
spec->multiout.dac_nids = spec->dac_nids;
|
||||
|
||||
switch (spec->board_config) {
|
||||
case STAC_D965_3ST:
|
||||
case STAC_D965_5ST:
|
||||
/* GPIO0 High = Enable EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = 0x01;
|
||||
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x01;
|
||||
spec->gpio_data = 0x01;
|
||||
spec->num_dmics = 0;
|
||||
|
||||
|
@ -3591,14 +3848,23 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
spec->mixer = stac927x_mixer;
|
||||
break;
|
||||
case STAC_DELL_BIOS:
|
||||
switch (codec->subsystem_id) {
|
||||
case 0x10280209:
|
||||
case 0x1028022e:
|
||||
/* correct the device field to SPDIF out */
|
||||
stac92xx_set_config_reg(codec, 0x21, 0x01442070);
|
||||
break;
|
||||
};
|
||||
/* configure the analog microphone on some laptops */
|
||||
stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
|
||||
/* correct the front output jack as a hp out */
|
||||
stac92xx_set_config_reg(codec, 0x0f, 0x02270110);
|
||||
stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
|
||||
/* correct the front input jack as a mic */
|
||||
stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
|
||||
/* fallthru */
|
||||
case STAC_DELL_3ST:
|
||||
/* GPIO2 High = Enable EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = 0x04;
|
||||
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04;
|
||||
spec->gpio_data = 0x04;
|
||||
spec->dmic_nids = stac927x_dmic_nids;
|
||||
spec->num_dmics = STAC927X_NUM_DMICS;
|
||||
|
@ -3610,7 +3876,7 @@ static int patch_stac927x(struct hda_codec *codec)
|
|||
break;
|
||||
default:
|
||||
/* GPIO0 High = Enable EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->gpio_data = 0x01;
|
||||
spec->num_dmics = 0;
|
||||
|
||||
|
@ -3714,6 +3980,7 @@ static int patch_stac9205(struct hda_codec *codec)
|
|||
(AC_USRSP_EN | STAC_HP_EVENT));
|
||||
|
||||
spec->gpio_dir = 0x0b;
|
||||
spec->eapd_mask = 0x01;
|
||||
spec->gpio_mask = 0x1b;
|
||||
spec->gpio_mute = 0x10;
|
||||
/* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
|
||||
|
@ -3723,7 +3990,7 @@ static int patch_stac9205(struct hda_codec *codec)
|
|||
break;
|
||||
default:
|
||||
/* GPIO0 High = EAPD */
|
||||
spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
|
||||
spec->gpio_data = 0x01;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include <sound/core.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
#include "hda_patch.h"
|
||||
|
||||
/* amp values */
|
||||
#define AMP_VAL_IDX_SHIFT 19
|
||||
|
@ -357,7 +357,8 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
|
||||
return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
|
||||
hinfo);
|
||||
}
|
||||
|
||||
static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
|
@ -430,8 +431,7 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
|
|||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct via_spec *spec = codec->spec;
|
||||
snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
|
||||
0, 0, 0);
|
||||
snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -493,6 +493,11 @@ static int via_build_controls(struct hda_codec *codec)
|
|||
spec->multiout.dig_out_nid);
|
||||
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_nid) {
|
||||
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
|
||||
|
@ -523,6 +528,7 @@ static int via_build_pcms(struct hda_codec *codec)
|
|||
codec->num_pcms++;
|
||||
info++;
|
||||
info->name = spec->stream_name_digital;
|
||||
info->pcm_type = HDA_PCM_TYPE_SPDIF;
|
||||
if (spec->multiout.dig_out_nid) {
|
||||
info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
|
||||
*(spec->stream_digital_playback);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* ALSA driver for ICEnsemble ICE1712 (Envy24)
|
||||
*
|
||||
* Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
|
||||
* Digigram VX442
|
||||
* Lowlevel functions for M-Audio Delta 1010, 1010E, 44, 66, 66E, Dio2496,
|
||||
* Audiophile, Digigram VX442
|
||||
*
|
||||
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
|
@ -86,6 +86,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
|
|||
unsigned char tmp;
|
||||
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
|
||||
switch (ice->eeprom.subvendor) {
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTA1010LT:
|
||||
tmp &= ~ICE1712_DELTA_1010LT_CS;
|
||||
tmp |= ICE1712_DELTA_1010LT_CCLK | ICE1712_DELTA_1010LT_CS_CS8427;
|
||||
|
@ -109,6 +110,7 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
|
|||
static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
|
||||
{
|
||||
switch (ice->eeprom.subvendor) {
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTA1010LT:
|
||||
tmp &= ~ICE1712_DELTA_1010LT_CS;
|
||||
tmp |= ICE1712_DELTA_1010LT_CS_NONE;
|
||||
|
@ -534,6 +536,14 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
int err;
|
||||
struct snd_akm4xxx *ak;
|
||||
|
||||
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 &&
|
||||
ice->eeprom.gpiodir == 0x7b)
|
||||
ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA1010E;
|
||||
|
||||
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA66 &&
|
||||
ice->eeprom.gpiodir == 0xfb)
|
||||
ice->eeprom.subvendor = ICE1712_SUBDEVICE_DELTA66E;
|
||||
|
||||
/* determine I2C, DACs and ADCs */
|
||||
switch (ice->eeprom.subvendor) {
|
||||
case ICE1712_SUBDEVICE_AUDIOPHILE:
|
||||
|
@ -550,6 +560,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
ice->num_total_adcs = ice->omni ? 8 : 4;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_DELTA1010:
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTA1010LT:
|
||||
case ICE1712_SUBDEVICE_MEDIASTATION:
|
||||
ice->num_total_dacs = 8;
|
||||
|
@ -559,6 +570,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
ice->num_total_dacs = 4; /* two AK4324 codecs */
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */
|
||||
ice->num_total_dacs = 4;
|
||||
ice->num_total_adcs = 4;
|
||||
break;
|
||||
|
@ -568,8 +580,10 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
switch (ice->eeprom.subvendor) {
|
||||
case ICE1712_SUBDEVICE_AUDIOPHILE:
|
||||
case ICE1712_SUBDEVICE_DELTA410:
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTA1010LT:
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
|
||||
snd_printk(KERN_ERR "unable to create I2C bus\n");
|
||||
return err;
|
||||
|
@ -601,6 +615,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
/* no analog? */
|
||||
switch (ice->eeprom.subvendor) {
|
||||
case ICE1712_SUBDEVICE_DELTA1010:
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTADIO2496:
|
||||
case ICE1712_SUBDEVICE_MEDIASTATION:
|
||||
return 0;
|
||||
|
@ -627,6 +642,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
|
|||
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
|
||||
break;
|
||||
default:
|
||||
|
@ -674,6 +690,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
|
|||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_DELTA1010E:
|
||||
case ICE1712_SUBDEVICE_DELTA1010LT:
|
||||
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010lt_wordclock_select, ice));
|
||||
if (err < 0)
|
||||
|
@ -716,6 +733,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_DELTA44:
|
||||
case ICE1712_SUBDEVICE_DELTA66:
|
||||
case ICE1712_SUBDEVICE_VX442:
|
||||
case ICE1712_SUBDEVICE_DELTA66E:
|
||||
err = snd_ice1712_akm4xxx_build_controls(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
|
@ -36,8 +36,10 @@
|
|||
"{Lionstracs,Mediastation},"
|
||||
|
||||
#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
|
||||
#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6
|
||||
#define ICE1712_SUBDEVICE_DELTADIO2496 0x121431d6
|
||||
#define ICE1712_SUBDEVICE_DELTA66 0x121432d6
|
||||
#define ICE1712_SUBDEVICE_DELTA66E 0xff1432d6
|
||||
#define ICE1712_SUBDEVICE_DELTA44 0x121433d6
|
||||
#define ICE1712_SUBDEVICE_AUDIOPHILE 0x121434d6
|
||||
#define ICE1712_SUBDEVICE_DELTA410 0x121438d6
|
||||
|
|
|
@ -238,6 +238,7 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1)
|
||||
!= 1)
|
||||
goto _error;
|
||||
|
@ -433,6 +434,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
ice->num_total_dacs = 8;
|
||||
ice->num_total_adcs = 8;
|
||||
break;
|
||||
|
@ -475,6 +477,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
|
||||
err = snd_i2c_device_create(ice->i2c, "CS8404",
|
||||
ICE1712_EWS88MT_CS8404_ADDR,
|
||||
&spec->i2cdevs[EWS_I2C_CS8404]);
|
||||
|
@ -518,6 +522,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
case ICE1712_SUBDEVICE_EWS88D:
|
||||
/* set up CS8404 */
|
||||
ice->spdif.ops.open = ews88_open_spdif;
|
||||
|
@ -547,6 +552,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice);
|
||||
break;
|
||||
case ICE1712_SUBDEVICE_EWX2496:
|
||||
|
@ -973,6 +979,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
case ICE1712_SUBDEVICE_DMX6FIRE:
|
||||
err = snd_ice1712_akm4xxx_build_controls(ice);
|
||||
if (err < 0)
|
||||
|
@ -992,6 +999,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
|
|||
case ICE1712_SUBDEVICE_EWS88MT:
|
||||
case ICE1712_SUBDEVICE_EWS88MT_NEW:
|
||||
case ICE1712_SUBDEVICE_PHASE88:
|
||||
case ICE1712_SUBDEVICE_TS88:
|
||||
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1048,6 +1056,13 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
|
|||
.chip_init = snd_ice1712_ews_init,
|
||||
.build_controls = snd_ice1712_ews_add_controls,
|
||||
},
|
||||
{
|
||||
.subvendor = ICE1712_SUBDEVICE_TS88,
|
||||
.name = "terrasoniq TS88",
|
||||
.model = "phase88",
|
||||
.chip_init = snd_ice1712_ews_init,
|
||||
.build_controls = snd_ice1712_ews_add_controls,
|
||||
},
|
||||
{
|
||||
.subvendor = ICE1712_SUBDEVICE_EWS88D,
|
||||
.name = "TerraTec EWS88D",
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
"{TerraTec,EWS 88MT},"\
|
||||
"{TerraTec,EWS 88D},"\
|
||||
"{TerraTec,DMX 6Fire},"\
|
||||
"{TerraTec,Phase 88},"
|
||||
"{TerraTec,Phase 88}," \
|
||||
"{terrasoniq,TS 88},"
|
||||
|
||||
#define ICE1712_SUBDEVICE_EWX2496 0x3b153011
|
||||
#define ICE1712_SUBDEVICE_EWS88MT 0x3b151511
|
||||
|
@ -38,6 +39,7 @@
|
|||
#define ICE1712_SUBDEVICE_EWS88D 0x3b152b11
|
||||
#define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811
|
||||
#define ICE1712_SUBDEVICE_PHASE88 0x3b155111
|
||||
#define ICE1712_SUBDEVICE_TS88 0x3b157c11
|
||||
|
||||
/* entry point */
|
||||
extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
|
||||
|
|
|
@ -208,6 +208,19 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
|
|||
/* ICE1712_STDSP24_MUTE |
|
||||
ICE1712_STDSP24_INSEL |
|
||||
ICE1712_STDSP24_DAREAR; */
|
||||
/* These boxconfigs have caused problems in the past.
|
||||
* The code is not optimal, but should now enable a working config to
|
||||
* be achieved.
|
||||
* ** MIDI IN can only be configured on one box **
|
||||
* ICE1712_STDSP24_BOX_MIDI1 needs to be set for that box.
|
||||
* Tests on a ADAC2000 box suggest the box config flags do not
|
||||
* work as would be expected, and the inputs are crossed.
|
||||
* Setting ICE1712_STDSP24_BOX_MIDI1 and ICE1712_STDSP24_BOX_MIDI2
|
||||
* on the same box connects MIDI-In to both 401 uarts; both outputs
|
||||
* are then active on all boxes.
|
||||
* The default config here sets up everything on the first box.
|
||||
* Alan Horstmann 5.2.2008
|
||||
*/
|
||||
spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
|
||||
ICE1712_STDSP24_BOX_CHN2 |
|
||||
ICE1712_STDSP24_BOX_CHN3 |
|
||||
|
@ -223,14 +236,14 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
|
|||
(spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0);
|
||||
snd_ice1712_stdsp24_insel(ice,
|
||||
(spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0);
|
||||
for (box = 0; box < 1; box++) {
|
||||
for (box = 0; box < 4; box++) {
|
||||
if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2)
|
||||
snd_ice1712_stdsp24_midi2(ice, 1);
|
||||
for (chn = 0; chn < 4; chn++)
|
||||
snd_ice1712_stdsp24_box_channel(ice, box, chn,
|
||||
(spec->boxconfig[box] & (1 << chn)) ? 1 : 0);
|
||||
snd_ice1712_stdsp24_box_midi(ice, box,
|
||||
(spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0);
|
||||
if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1)
|
||||
snd_ice1712_stdsp24_box_midi(ice, box, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -322,6 +335,8 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
|
|||
.name = "Hoontech SoundTrack Audio DSP24",
|
||||
.model = "dsp24",
|
||||
.chip_init = snd_ice1712_hoontech_init,
|
||||
.mpu401_1_name = "MIDI-1 Hoontech/STA DSP24",
|
||||
.mpu401_2_name = "MIDI-2 Hoontech/STA DSP24",
|
||||
},
|
||||
{
|
||||
.subvendor = ICE1712_SUBDEVICE_STDSP24_VALUE, /* a dummy id */
|
||||
|
|
|
@ -1297,11 +1297,14 @@ static void snd_ice1712_update_volume(struct snd_ice1712 *ice, int index)
|
|||
static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
|
||||
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
|
||||
kcontrol->private_value;
|
||||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
|
||||
ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
|
||||
ucontrol->value.integer.value[0] =
|
||||
!((ice->pro_volumes[priv_idx] >> 15) & 1);
|
||||
ucontrol->value.integer.value[1] =
|
||||
!((ice->pro_volumes[priv_idx] >> 31) & 1);
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1309,16 +1312,17 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc
|
|||
static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
|
||||
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
|
||||
kcontrol->private_value;
|
||||
unsigned int nval, change;
|
||||
|
||||
nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
|
||||
(ucontrol->value.integer.value[1] ? 0 : 0x80000000);
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
nval |= ice->pro_volumes[index] & ~0x80008000;
|
||||
change = nval != ice->pro_volumes[index];
|
||||
ice->pro_volumes[index] = nval;
|
||||
snd_ice1712_update_volume(ice, index);
|
||||
nval |= ice->pro_volumes[priv_idx] & ~0x80008000;
|
||||
change = nval != ice->pro_volumes[priv_idx];
|
||||
ice->pro_volumes[priv_idx] = nval;
|
||||
snd_ice1712_update_volume(ice, priv_idx);
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
return change;
|
||||
}
|
||||
|
@ -1335,11 +1339,14 @@ static int snd_ice1712_pro_mixer_volume_info(struct snd_kcontrol *kcontrol, stru
|
|||
static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
|
||||
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
|
||||
kcontrol->private_value;
|
||||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
|
||||
ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
|
||||
ucontrol->value.integer.value[0] =
|
||||
(ice->pro_volumes[priv_idx] >> 0) & 127;
|
||||
ucontrol->value.integer.value[1] =
|
||||
(ice->pro_volumes[priv_idx] >> 16) & 127;
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1347,16 +1354,17 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc
|
|||
static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
int index = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + kcontrol->private_value;
|
||||
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
|
||||
kcontrol->private_value;
|
||||
unsigned int nval, change;
|
||||
|
||||
nval = (ucontrol->value.integer.value[0] & 127) |
|
||||
((ucontrol->value.integer.value[1] & 127) << 16);
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
nval |= ice->pro_volumes[index] & ~0x007f007f;
|
||||
change = nval != ice->pro_volumes[index];
|
||||
ice->pro_volumes[index] = nval;
|
||||
snd_ice1712_update_volume(ice, index);
|
||||
nval |= ice->pro_volumes[priv_idx] & ~0x007f007f;
|
||||
change = nval != ice->pro_volumes[priv_idx];
|
||||
ice->pro_volumes[priv_idx] = nval;
|
||||
snd_ice1712_update_volume(ice, priv_idx);
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
return change;
|
||||
}
|
||||
|
@ -2482,10 +2490,9 @@ static int snd_ice1712_free(struct snd_ice1712 *ice)
|
|||
outb(0xff, ICEREG(ice, IRQMASK));
|
||||
/* --- */
|
||||
__hw_end:
|
||||
if (ice->irq >= 0) {
|
||||
synchronize_irq(ice->irq);
|
||||
if (ice->irq >= 0)
|
||||
free_irq(ice->irq, ice);
|
||||
}
|
||||
|
||||
if (ice->port)
|
||||
pci_release_regions(ice->pci);
|
||||
snd_ice1712_akm4xxx_free(ice);
|
||||
|
|
|
@ -367,6 +367,15 @@ struct snd_ice1712 {
|
|||
|
||||
/* other board-specific data */
|
||||
void *spec;
|
||||
|
||||
/* VT172x specific */
|
||||
int pro_rate_default;
|
||||
int (*is_spdif_master)(struct snd_ice1712 *ice);
|
||||
unsigned int (*get_rate)(struct snd_ice1712 *ice);
|
||||
void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
|
||||
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
|
||||
void (*set_spdif_clock)(struct snd_ice1712 *ice);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -429,10 +438,14 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
|
|||
static inline void snd_ice1712_gpio_write_bits(struct snd_ice1712 *ice,
|
||||
unsigned int mask, unsigned int bits)
|
||||
{
|
||||
unsigned val;
|
||||
|
||||
ice->gpio.direction |= mask;
|
||||
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
|
||||
snd_ice1712_gpio_set_mask(ice, ~mask);
|
||||
snd_ice1712_gpio_write(ice, mask & bits);
|
||||
val = snd_ice1712_gpio_read(ice);
|
||||
val &= ~mask;
|
||||
val |= mask & bits;
|
||||
snd_ice1712_gpio_write(ice, val);
|
||||
}
|
||||
|
||||
static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,
|
||||
|
|
|
@ -106,15 +106,19 @@ static unsigned int PRO_RATE_DEFAULT = 44100;
|
|||
* Basic I/O
|
||||
*/
|
||||
|
||||
/*
|
||||
* default rates, default clock routines
|
||||
*/
|
||||
|
||||
/* check whether the clock mode is spdif-in */
|
||||
static inline int is_spdif_master(struct snd_ice1712 *ice)
|
||||
static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
|
||||
{
|
||||
return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
|
||||
{
|
||||
return is_spdif_master(ice) || PRO_RATE_LOCKED;
|
||||
return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -218,6 +222,32 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
|
|||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* MPU401 accessor
|
||||
*/
|
||||
static unsigned char snd_vt1724_mpu401_read(struct snd_mpu401 *mpu,
|
||||
unsigned long addr)
|
||||
{
|
||||
/* fix status bits to the standard position */
|
||||
/* only RX_EMPTY and TX_FULL are checked */
|
||||
if (addr == MPU401C(mpu))
|
||||
return (inb(addr) & 0x0c) << 4;
|
||||
else
|
||||
return inb(addr);
|
||||
}
|
||||
|
||||
static void snd_vt1724_mpu401_write(struct snd_mpu401 *mpu,
|
||||
unsigned char data, unsigned long addr)
|
||||
{
|
||||
if (addr == MPU401C(mpu)) {
|
||||
if (data == MPU401_ENTER_UART)
|
||||
outb(0x01, addr);
|
||||
/* what else? */
|
||||
} else
|
||||
outb(data, addr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Interrupt handler
|
||||
*/
|
||||
|
@ -226,24 +256,53 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
|
|||
{
|
||||
struct snd_ice1712 *ice = dev_id;
|
||||
unsigned char status;
|
||||
unsigned char status_mask =
|
||||
VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
|
||||
int handled = 0;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
int timeout = 0;
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
status = inb(ICEREG1724(ice, IRQSTAT));
|
||||
status &= status_mask;
|
||||
if (status == 0)
|
||||
break;
|
||||
|
||||
handled = 1;
|
||||
/* these should probably be separated at some point,
|
||||
* but as we don't currently have MPU support on the board
|
||||
* I will leave it
|
||||
*/
|
||||
if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {
|
||||
if (ice->rmidi[0])
|
||||
snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data);
|
||||
outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));
|
||||
status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (++timeout > 10) {
|
||||
printk(KERN_ERR
|
||||
"ice1724: Too long irq loop, status = 0x%x\n",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
handled = 1;
|
||||
if (status & VT1724_IRQ_MPU_TX) {
|
||||
if (ice->rmidi[0])
|
||||
snd_mpu401_uart_interrupt_tx(irq,
|
||||
ice->rmidi[0]->private_data);
|
||||
else /* disable TX to be sure */
|
||||
outb(inb(ICEREG1724(ice, IRQMASK)) |
|
||||
VT1724_IRQ_MPU_TX,
|
||||
ICEREG1724(ice, IRQMASK));
|
||||
/* Due to mysterical reasons, MPU_TX is always
|
||||
* generated (and can't be cleared) when a PCM
|
||||
* playback is going. So let's ignore at the
|
||||
* next loop.
|
||||
*/
|
||||
status_mask &= ~VT1724_IRQ_MPU_TX;
|
||||
}
|
||||
if (status & VT1724_IRQ_MPU_RX) {
|
||||
if (ice->rmidi[0])
|
||||
snd_mpu401_uart_interrupt(irq,
|
||||
ice->rmidi[0]->private_data);
|
||||
else /* disable RX to be sure */
|
||||
outb(inb(ICEREG1724(ice, IRQMASK)) |
|
||||
VT1724_IRQ_MPU_RX,
|
||||
ICEREG1724(ice, IRQMASK));
|
||||
}
|
||||
/* ack MPU irq */
|
||||
outb(status, ICEREG1724(ice, IRQSTAT));
|
||||
if (status & VT1724_IRQ_MTPCM) {
|
||||
/*
|
||||
* Multi-track PCM
|
||||
|
@ -391,51 +450,61 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
|
||||
VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
|
||||
|
||||
static int get_max_rate(struct snd_ice1712 *ice)
|
||||
static const unsigned int stdclock_rate_list[16] = {
|
||||
48000, 24000, 12000, 9600, 32000, 16000, 8000, 96000, 44100,
|
||||
22050, 11025, 88200, 176400, 0, 192000, 64000
|
||||
};
|
||||
|
||||
static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
|
||||
{
|
||||
unsigned int rate;
|
||||
rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
|
||||
return rate;
|
||||
}
|
||||
|
||||
static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(stdclock_rate_list); i++) {
|
||||
if (stdclock_rate_list[i] == rate) {
|
||||
outb(i, ICEMT1724(ice, RATE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
|
||||
unsigned int rate)
|
||||
{
|
||||
unsigned char val, old;
|
||||
/* check MT02 */
|
||||
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
|
||||
if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
|
||||
return 192000;
|
||||
val = old = inb(ICEMT1724(ice, I2S_FORMAT));
|
||||
if (rate > 96000)
|
||||
val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
|
||||
else
|
||||
return 96000;
|
||||
} else
|
||||
return 48000;
|
||||
val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
|
||||
if (val != old) {
|
||||
outb(val, ICEMT1724(ice, I2S_FORMAT));
|
||||
/* master clock changed */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* no change in master clock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
||||
int force)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char val, old;
|
||||
unsigned int i, mclk_change;
|
||||
unsigned char mclk_change;
|
||||
unsigned int i, old_rate;
|
||||
|
||||
if (rate > get_max_rate(ice))
|
||||
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
|
||||
return;
|
||||
|
||||
switch (rate) {
|
||||
case 8000: val = 6; break;
|
||||
case 9600: val = 3; break;
|
||||
case 11025: val = 10; break;
|
||||
case 12000: val = 2; break;
|
||||
case 16000: val = 5; break;
|
||||
case 22050: val = 9; break;
|
||||
case 24000: val = 1; break;
|
||||
case 32000: val = 4; break;
|
||||
case 44100: val = 8; break;
|
||||
case 48000: val = 0; break;
|
||||
case 64000: val = 15; break;
|
||||
case 88200: val = 11; break;
|
||||
case 96000: val = 7; break;
|
||||
case 176400: val = 12; break;
|
||||
case 192000: val = 14; break;
|
||||
default:
|
||||
snd_BUG();
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ice->reg_lock, flags);
|
||||
if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
|
||||
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);
|
||||
|
@ -446,9 +515,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
return;
|
||||
}
|
||||
|
||||
old = inb(ICEMT1724(ice, RATE));
|
||||
if (force || old != val)
|
||||
outb(val, ICEMT1724(ice, RATE));
|
||||
old_rate = ice->get_rate(ice);
|
||||
if (force || (old_rate != rate))
|
||||
ice->set_rate(ice, rate);
|
||||
else if (rate == ice->cur_rate) {
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
return;
|
||||
|
@ -456,19 +525,9 @@ static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|||
|
||||
ice->cur_rate = rate;
|
||||
|
||||
/* check MT02 */
|
||||
mclk_change = 0;
|
||||
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
|
||||
val = old = inb(ICEMT1724(ice, I2S_FORMAT));
|
||||
if (rate > 96000)
|
||||
val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
|
||||
else
|
||||
val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
|
||||
if (val != old) {
|
||||
outb(val, ICEMT1724(ice, I2S_FORMAT));
|
||||
mclk_change = 1;
|
||||
}
|
||||
}
|
||||
/* setting master clock */
|
||||
mclk_change = ice->set_mclk(ice, rate);
|
||||
|
||||
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
||||
|
||||
if (mclk_change && ice->gpio.i2s_mclk_changed)
|
||||
|
@ -727,43 +786,32 @@ static const struct snd_pcm_hardware snd_vt1724_2ch_stereo =
|
|||
/*
|
||||
* set rate constraints
|
||||
*/
|
||||
static int set_rate_constraints(struct snd_ice1712 *ice,
|
||||
struct snd_pcm_substream *substream)
|
||||
static void set_std_hw_rates(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
if (ice->hw_rates) {
|
||||
/* hardware specific */
|
||||
runtime->hw.rate_min = ice->hw_rates->list[0];
|
||||
runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
ice->hw_rates);
|
||||
}
|
||||
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
|
||||
/* I2S */
|
||||
/* VT1720 doesn't support more than 96kHz */
|
||||
if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_constraints_rates_192);
|
||||
else {
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_KNOT |
|
||||
SNDRV_PCM_RATE_8000_96000;
|
||||
runtime->hw.rate_max = 96000;
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_constraints_rates_96);
|
||||
}
|
||||
} else if (ice->ac97) {
|
||||
ice->hw_rates = &hw_constraints_rates_192;
|
||||
else
|
||||
ice->hw_rates = &hw_constraints_rates_96;
|
||||
} else {
|
||||
/* ACLINK */
|
||||
runtime->hw.rate_max = 48000;
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000;
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hw_constraints_rates_48);
|
||||
ice->hw_rates = &hw_constraints_rates_48;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_rate_constraints(struct snd_ice1712 *ice,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
runtime->hw.rate_min = ice->hw_rates->list[0];
|
||||
runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
|
||||
runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
|
||||
return snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
ice->hw_rates);
|
||||
}
|
||||
|
||||
/* multi-channel playback needs alignment 8x32bit regardless of the channels
|
||||
|
@ -824,7 +872,7 @@ static int snd_vt1724_playback_pro_close(struct snd_pcm_substream *substream)
|
|||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (PRO_RATE_RESET)
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
||||
ice->playback_pro_substream = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -835,7 +883,7 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
|
|||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (PRO_RATE_RESET)
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
||||
ice->capture_pro_substream = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
@ -970,6 +1018,8 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
|
|||
VT1724_BUFFER_ALIGN);
|
||||
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
VT1724_BUFFER_ALIGN);
|
||||
if (ice->spdif.ops.open)
|
||||
ice->spdif.ops.open(ice, substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -978,8 +1028,10 @@ static int snd_vt1724_playback_spdif_close(struct snd_pcm_substream *substream)
|
|||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (PRO_RATE_RESET)
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
||||
ice->playback_con_substream = NULL;
|
||||
if (ice->spdif.ops.close)
|
||||
ice->spdif.ops.close(ice, substream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1002,6 +1054,8 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
|
|||
VT1724_BUFFER_ALIGN);
|
||||
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
||||
VT1724_BUFFER_ALIGN);
|
||||
if (ice->spdif.ops.open)
|
||||
ice->spdif.ops.open(ice, substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1010,8 +1064,10 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
|
|||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (PRO_RATE_RESET)
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
||||
ice->capture_con_substream = NULL;
|
||||
if (ice->spdif.ops.close)
|
||||
ice->spdif.ops.close(ice, substream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1154,7 +1210,7 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
|
|||
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (PRO_RATE_RESET)
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
||||
ice->playback_con_substream_ds[substream->number] = NULL;
|
||||
ice->pcm_reserved[substream->number] = NULL;
|
||||
|
||||
|
@ -1572,50 +1628,18 @@ int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol,
|
|||
static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static const char * const texts_1724[] = {
|
||||
"8000", /* 0: 6 */
|
||||
"9600", /* 1: 3 */
|
||||
"11025", /* 2: 10 */
|
||||
"12000", /* 3: 2 */
|
||||
"16000", /* 4: 5 */
|
||||
"22050", /* 5: 9 */
|
||||
"24000", /* 6: 1 */
|
||||
"32000", /* 7: 4 */
|
||||
"44100", /* 8: 8 */
|
||||
"48000", /* 9: 0 */
|
||||
"64000", /* 10: 15 */
|
||||
"88200", /* 11: 11 */
|
||||
"96000", /* 12: 7 */
|
||||
"176400", /* 13: 12 */
|
||||
"192000", /* 14: 14 */
|
||||
"IEC958 Input", /* 15: -- */
|
||||
};
|
||||
static const char * const texts_1720[] = {
|
||||
"8000", /* 0: 6 */
|
||||
"9600", /* 1: 3 */
|
||||
"11025", /* 2: 10 */
|
||||
"12000", /* 3: 2 */
|
||||
"16000", /* 4: 5 */
|
||||
"22050", /* 5: 9 */
|
||||
"24000", /* 6: 1 */
|
||||
"32000", /* 7: 4 */
|
||||
"44100", /* 8: 8 */
|
||||
"48000", /* 9: 0 */
|
||||
"64000", /* 10: 15 */
|
||||
"88200", /* 11: 11 */
|
||||
"96000", /* 12: 7 */
|
||||
"IEC958 Input", /* 13: -- */
|
||||
};
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16;
|
||||
uinfo->value.enumerated.items = ice->hw_rates->count + 1;
|
||||
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
||||
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] :
|
||||
texts_1724[uinfo->value.enumerated.item]);
|
||||
if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
|
||||
strcpy(uinfo->value.enumerated.name, "IEC958 Input");
|
||||
else
|
||||
sprintf(uinfo->value.enumerated.name, "%d",
|
||||
ice->hw_rates->list[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1623,68 +1647,79 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
|
|||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
static const unsigned char xlate[16] = {
|
||||
9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 13, 255, 14, 10
|
||||
};
|
||||
unsigned char val;
|
||||
unsigned int i, rate;
|
||||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
if (is_spdif_master(ice)) {
|
||||
ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15;
|
||||
if (ice->is_spdif_master(ice)) {
|
||||
ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
|
||||
} else {
|
||||
val = xlate[inb(ICEMT1724(ice, RATE)) & 15];
|
||||
if (val == 255) {
|
||||
snd_BUG();
|
||||
val = 0;
|
||||
rate = ice->get_rate(ice);
|
||||
ucontrol->value.enumerated.item[0] = 0;
|
||||
for (i = 0; i < ice->hw_rates->count; i++) {
|
||||
if (ice->hw_rates->list[i] == rate) {
|
||||
ucontrol->value.enumerated.item[0] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ucontrol->value.enumerated.item[0] = val;
|
||||
}
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setting clock to external - SPDIF */
|
||||
static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
|
||||
{
|
||||
unsigned char oval;
|
||||
unsigned char i2s_oval;
|
||||
oval = inb(ICEMT1724(ice, RATE));
|
||||
outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
|
||||
/* setting 256fs */
|
||||
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
|
||||
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
|
||||
}
|
||||
|
||||
static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
unsigned char oval;
|
||||
int rate;
|
||||
int change = 0;
|
||||
int spdif = ice->vt1720 ? 13 : 15;
|
||||
unsigned int old_rate, new_rate;
|
||||
unsigned int item = ucontrol->value.enumerated.item[0];
|
||||
unsigned int spdif = ice->hw_rates->count;
|
||||
|
||||
if (item > spdif)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
oval = inb(ICEMT1724(ice, RATE));
|
||||
if (ucontrol->value.enumerated.item[0] == spdif) {
|
||||
unsigned char i2s_oval;
|
||||
outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
|
||||
/* setting 256fs */
|
||||
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
|
||||
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X,
|
||||
ICEMT1724(ice, I2S_FORMAT));
|
||||
if (ice->is_spdif_master(ice))
|
||||
old_rate = 0;
|
||||
else
|
||||
old_rate = ice->get_rate(ice);
|
||||
if (item == spdif) {
|
||||
/* switching to external clock via SPDIF */
|
||||
ice->set_spdif_clock(ice);
|
||||
new_rate = 0;
|
||||
} else {
|
||||
rate = rates[ucontrol->value.integer.value[0] % 15];
|
||||
if (rate <= get_max_rate(ice)) {
|
||||
PRO_RATE_DEFAULT = rate;
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1);
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
}
|
||||
/* internal on-card clock */
|
||||
new_rate = ice->hw_rates->list[item];
|
||||
ice->pro_rate_default = new_rate;
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
|
||||
spin_lock_irq(&ice->reg_lock);
|
||||
}
|
||||
change = inb(ICEMT1724(ice, RATE)) != oval;
|
||||
spin_unlock_irq(&ice->reg_lock);
|
||||
|
||||
if ((oval & VT1724_SPDIF_MASTER) !=
|
||||
(inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER)) {
|
||||
/* the first reset to the SPDIF master mode? */
|
||||
if (old_rate != new_rate && !new_rate) {
|
||||
/* notify akm chips as well */
|
||||
if (is_spdif_master(ice)) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < ice->akm_codecs; i++) {
|
||||
if (ice->akm[i].ops.set_rate_val)
|
||||
ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
|
||||
}
|
||||
unsigned int i;
|
||||
if (ice->gpio.set_pro_rate)
|
||||
ice->gpio.set_pro_rate(ice, 0);
|
||||
for (i = 0; i < ice->akm_codecs; i++) {
|
||||
if (ice->akm[i].ops.set_rate_val)
|
||||
ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
|
||||
}
|
||||
}
|
||||
return change;
|
||||
return old_rate != new_rate;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
|
||||
|
@ -2065,12 +2100,16 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
|
|||
|
||||
|
||||
|
||||
static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
||||
static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
||||
{
|
||||
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
|
||||
udelay(200);
|
||||
msleep(10);
|
||||
outb(0, ICEREG1724(ice, CONTROL));
|
||||
udelay(200);
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
|
||||
outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
|
||||
outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES));
|
||||
|
@ -2169,10 +2208,8 @@ static int snd_vt1724_free(struct snd_ice1712 *ice)
|
|||
outb(0xff, ICEREG1724(ice, IRQMASK));
|
||||
/* --- */
|
||||
__hw_end:
|
||||
if (ice->irq >= 0) {
|
||||
synchronize_irq(ice->irq);
|
||||
if (ice->irq >= 0)
|
||||
free_irq(ice->irq, ice);
|
||||
}
|
||||
pci_release_regions(ice->pci);
|
||||
snd_ice1712_akm4xxx_free(ice);
|
||||
pci_disable_device(ice->pci);
|
||||
|
@ -2243,6 +2280,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
|||
|
||||
ice->irq = pci->irq;
|
||||
|
||||
snd_vt1724_chip_reset(ice);
|
||||
if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
|
||||
snd_vt1724_free(ice);
|
||||
return -EIO;
|
||||
|
@ -2253,10 +2291,7 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
|
|||
}
|
||||
|
||||
/* unmask used interrupts */
|
||||
if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401))
|
||||
mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
|
||||
else
|
||||
mask = 0;
|
||||
mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
|
||||
outb(mask, ICEREG1724(ice, IRQMASK));
|
||||
/* don't handle FIFO overrun/underruns (just yet),
|
||||
* since they cause machine lockups
|
||||
|
@ -2335,6 +2370,19 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
|||
* was called so in ice1712 driver, and vt1724 driver is derived from
|
||||
* ice1712 driver.
|
||||
*/
|
||||
ice->pro_rate_default = PRO_RATE_DEFAULT;
|
||||
if (!ice->is_spdif_master)
|
||||
ice->is_spdif_master = stdclock_is_spdif_master;
|
||||
if (!ice->get_rate)
|
||||
ice->get_rate = stdclock_get_rate;
|
||||
if (!ice->set_rate)
|
||||
ice->set_rate = stdclock_set_rate;
|
||||
if (!ice->set_mclk)
|
||||
ice->set_mclk = stdclock_set_mclk;
|
||||
if (!ice->set_spdif_clock)
|
||||
ice->set_spdif_clock = stdclock_set_spdif_clock;
|
||||
if (!ice->hw_rates)
|
||||
set_std_hw_rates(ice);
|
||||
|
||||
if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
|
||||
snd_card_free(card);
|
||||
|
@ -2377,14 +2425,29 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
|||
|
||||
if (! c->no_mpu401) {
|
||||
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
|
||||
struct snd_mpu401 *mpu;
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
|
||||
ICEREG1724(ice, MPU_CTRL),
|
||||
MPU401_INFO_INTEGRATED,
|
||||
(MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_TX_IRQ),
|
||||
ice->irq, 0,
|
||||
&ice->rmidi[0])) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
mpu = ice->rmidi[0]->private_data;
|
||||
mpu->read = snd_vt1724_mpu401_read;
|
||||
mpu->write = snd_vt1724_mpu401_write;
|
||||
/* unmask MPU RX/TX irqs */
|
||||
outb(inb(ICEREG1724(ice, IRQMASK)) &
|
||||
~(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX),
|
||||
ICEREG1724(ice, IRQMASK));
|
||||
#if 0 /* for testing */
|
||||
/* set watermarks */
|
||||
outb(VT1724_MPU_RX_FIFO | 0x1,
|
||||
ICEREG1724(ice, MPU_FIFO_WM));
|
||||
outb(0x1, ICEREG1724(ice, MPU_FIFO_WM));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* Lowlevel functions for ESI Juli@ cards
|
||||
*
|
||||
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
|
||||
* 2008 Pavel Hofman <dustin@seznam.cz>
|
||||
*
|
||||
*
|
||||
* 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
|
||||
|
@ -27,11 +29,11 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "ice1712.h"
|
||||
#include "envy24ht.h"
|
||||
#include "juli.h"
|
||||
|
||||
struct juli_spec {
|
||||
struct ak4114 *ak4114;
|
||||
unsigned int analog: 1;
|
||||
|
@ -43,6 +45,32 @@ struct juli_spec {
|
|||
#define AK4114_ADDR 0x20 /* S/PDIF receiver */
|
||||
#define AK4358_ADDR 0x22 /* DAC */
|
||||
|
||||
/*
|
||||
* Juli does not use the standard ICE1724 clock scheme. Juli's ice1724 chip is
|
||||
* supplied by external clock provided by Xilinx array and MK73-1 PLL frequency
|
||||
* multiplier. Actual frequency is set by ice1724 GPIOs hooked to the Xilinx.
|
||||
*
|
||||
* The clock circuitry is supplied by the two ice1724 crystals. This
|
||||
* arrangement allows to generate independent clock signal for AK4114's input
|
||||
* rate detection circuit. As a result, Juli, unlike most other
|
||||
* ice1724+ak4114-based cards, detects spdif input rate correctly.
|
||||
* This fact is applied in the driver, allowing to modify PCM stream rate
|
||||
* parameter according to the actual input rate.
|
||||
*
|
||||
* Juli uses the remaining three stereo-channels of its DAC to optionally
|
||||
* monitor analog input, digital input, and digital output. The corresponding
|
||||
* I2S signals are routed by Xilinx, controlled by GPIOs.
|
||||
*
|
||||
* The master mute is implemented using output muting transistors (GPIO) in
|
||||
* combination with smuting the DAC.
|
||||
*
|
||||
* The card itself has no HW master volume control, implemented using the
|
||||
* vmaster control.
|
||||
*
|
||||
* TODO:
|
||||
* researching and fixing the input monitors
|
||||
*/
|
||||
|
||||
/*
|
||||
* GPIO pins
|
||||
*/
|
||||
|
@ -55,17 +83,82 @@ struct juli_spec {
|
|||
#define GPIO_MULTI_2X (1<<2)
|
||||
#define GPIO_MULTI_1X (2<<2) /* also external */
|
||||
#define GPIO_MULTI_HALF (3<<2)
|
||||
#define GPIO_INTERNAL_CLOCK (1<<4)
|
||||
#define GPIO_INTERNAL_CLOCK (1<<4) /* 0 = external, 1 = internal */
|
||||
#define GPIO_CLOCK_MASK (1<<4)
|
||||
#define GPIO_ANALOG_PRESENT (1<<5) /* RO only: 0 = present */
|
||||
#define GPIO_RXMCLK_SEL (1<<7) /* must be 0 */
|
||||
#define GPIO_AK5385A_CKS0 (1<<8)
|
||||
#define GPIO_AK5385A_DFS0 (1<<9) /* swapped with DFS1 according doc? */
|
||||
#define GPIO_AK5385A_DFS1 (1<<10)
|
||||
#define GPIO_AK5385A_DFS1 (1<<9)
|
||||
#define GPIO_AK5385A_DFS0 (1<<10)
|
||||
#define GPIO_DIGOUT_MONITOR (1<<11) /* 1 = active */
|
||||
#define GPIO_DIGIN_MONITOR (1<<12) /* 1 = active */
|
||||
#define GPIO_ANAIN_MONITOR (1<<13) /* 1 = active */
|
||||
#define GPIO_AK5385A_MCLK (1<<14) /* must be 0 */
|
||||
#define GPIO_MUTE_CONTROL (1<<15) /* 0 = off, 1 = on */
|
||||
#define GPIO_AK5385A_CKS1 (1<<14) /* must be 0 */
|
||||
#define GPIO_MUTE_CONTROL (1<<15) /* output mute, 1 = muted */
|
||||
|
||||
#define GPIO_RATE_MASK (GPIO_FREQ_MASK | GPIO_MULTI_MASK | \
|
||||
GPIO_CLOCK_MASK)
|
||||
#define GPIO_AK5385A_MASK (GPIO_AK5385A_CKS0 | GPIO_AK5385A_DFS0 | \
|
||||
GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS1)
|
||||
|
||||
#define JULI_PCM_RATE (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
|
||||
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
|
||||
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
|
||||
SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
|
||||
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
|
||||
|
||||
#define GPIO_RATE_16000 (GPIO_FREQ_32KHZ | GPIO_MULTI_HALF | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_22050 (GPIO_FREQ_44KHZ | GPIO_MULTI_HALF | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_24000 (GPIO_FREQ_48KHZ | GPIO_MULTI_HALF | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_32000 (GPIO_FREQ_32KHZ | GPIO_MULTI_1X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_44100 (GPIO_FREQ_44KHZ | GPIO_MULTI_1X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_48000 (GPIO_FREQ_48KHZ | GPIO_MULTI_1X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_64000 (GPIO_FREQ_32KHZ | GPIO_MULTI_2X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_88200 (GPIO_FREQ_44KHZ | GPIO_MULTI_2X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_96000 (GPIO_FREQ_48KHZ | GPIO_MULTI_2X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_176400 (GPIO_FREQ_44KHZ | GPIO_MULTI_4X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
#define GPIO_RATE_192000 (GPIO_FREQ_48KHZ | GPIO_MULTI_4X | \
|
||||
GPIO_INTERNAL_CLOCK)
|
||||
|
||||
/*
|
||||
* Initial setup of the conversion array GPIO <-> rate
|
||||
*/
|
||||
static unsigned int juli_rates[] = {
|
||||
16000, 22050, 24000, 32000,
|
||||
44100, 48000, 64000, 88200,
|
||||
96000, 176400, 192000,
|
||||
};
|
||||
|
||||
static unsigned int gpio_vals[] = {
|
||||
GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000,
|
||||
GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200,
|
||||
GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list juli_rates_info = {
|
||||
.count = ARRAY_SIZE(juli_rates),
|
||||
.list = juli_rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int get_gpio_val(int rate)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(juli_rates); i++)
|
||||
if (juli_rates[i] == rate)
|
||||
return gpio_vals[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
|
||||
{
|
||||
|
@ -77,6 +170,27 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
|
|||
return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* If SPDIF capture and slaved to SPDIF-IN, setting runtime rate
|
||||
* to the external rate
|
||||
*/
|
||||
static void juli_spdif_in_open(struct snd_ice1712 *ice,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct juli_spec *spec = ice->spec;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int rate;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
|
||||
!ice->is_spdif_master(ice))
|
||||
return;
|
||||
rate = snd_ak4114_external_rate(spec->ak4114);
|
||||
if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
|
||||
runtime->hw.rate_min = rate;
|
||||
runtime->hw.rate_max = rate;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AK4358 section
|
||||
*/
|
||||
|
@ -99,57 +213,285 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip,
|
|||
}
|
||||
|
||||
/*
|
||||
* change the rate of envy24HT, AK4358
|
||||
* change the rate of envy24HT, AK4358, AK5385
|
||||
*/
|
||||
static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
|
||||
{
|
||||
unsigned char old, tmp, dfs;
|
||||
unsigned char old, tmp, ak4358_dfs;
|
||||
unsigned int ak5385_pins, old_gpio, new_gpio;
|
||||
struct snd_ice1712 *ice = ak->private_data[0];
|
||||
struct juli_spec *spec = ice->spec;
|
||||
|
||||
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
|
||||
if (rate == 0) /* no hint - S/PDIF input is master or the new spdif
|
||||
input rate undetected, simply return */
|
||||
return;
|
||||
|
||||
|
||||
/* adjust DFS on codecs */
|
||||
if (rate > 96000)
|
||||
dfs = 2;
|
||||
else if (rate > 48000)
|
||||
dfs = 1;
|
||||
else
|
||||
dfs = 0;
|
||||
|
||||
if (rate > 96000) {
|
||||
ak4358_dfs = 2;
|
||||
ak5385_pins = GPIO_AK5385A_DFS1 | GPIO_AK5385A_CKS0;
|
||||
} else if (rate > 48000) {
|
||||
ak4358_dfs = 1;
|
||||
ak5385_pins = GPIO_AK5385A_DFS0;
|
||||
} else {
|
||||
ak4358_dfs = 0;
|
||||
ak5385_pins = 0;
|
||||
}
|
||||
/* AK5385 first, since it requires cold reset affecting both codecs */
|
||||
old_gpio = ice->gpio.get_data(ice);
|
||||
new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins;
|
||||
/* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
|
||||
new_gpio); */
|
||||
ice->gpio.set_data(ice, new_gpio);
|
||||
|
||||
/* cold reset */
|
||||
old = inb(ICEMT1724(ice, AC97_CMD));
|
||||
outb(old | VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
|
||||
udelay(1);
|
||||
outb(old & ~VT1724_AC97_COLD, ICEMT1724(ice, AC97_CMD));
|
||||
|
||||
/* AK4358 */
|
||||
/* set new value, reset DFS */
|
||||
tmp = snd_akm4xxx_get(ak, 0, 2);
|
||||
old = (tmp >> 4) & 0x03;
|
||||
if (old == dfs)
|
||||
return;
|
||||
/* reset DFS */
|
||||
snd_akm4xxx_reset(ak, 1);
|
||||
tmp = snd_akm4xxx_get(ak, 0, 2);
|
||||
tmp &= ~(0x03 << 4);
|
||||
tmp |= dfs << 4;
|
||||
tmp |= ak4358_dfs << 4;
|
||||
snd_akm4xxx_set(ak, 0, 2, tmp);
|
||||
snd_akm4xxx_reset(ak, 0);
|
||||
|
||||
/* reinit ak4114 */
|
||||
snd_ak4114_reinit(spec->ak4114);
|
||||
}
|
||||
|
||||
#define AK_DAC(xname, xch) { .name = xname, .num_channels = xch }
|
||||
#define PCM_VOLUME "PCM Playback Volume"
|
||||
#define MONITOR_AN_IN_VOLUME "Monitor Analog In Volume"
|
||||
#define MONITOR_DIG_IN_VOLUME "Monitor Digital In Volume"
|
||||
#define MONITOR_DIG_OUT_VOLUME "Monitor Digital Out Volume"
|
||||
|
||||
static const struct snd_akm4xxx_dac_channel juli_dac[] = {
|
||||
AK_DAC(PCM_VOLUME, 2),
|
||||
AK_DAC(MONITOR_AN_IN_VOLUME, 2),
|
||||
AK_DAC(MONITOR_DIG_OUT_VOLUME, 2),
|
||||
AK_DAC(MONITOR_DIG_IN_VOLUME, 2),
|
||||
};
|
||||
|
||||
|
||||
static struct snd_akm4xxx akm_juli_dac __devinitdata = {
|
||||
.type = SND_AK4358,
|
||||
.num_dacs = 2,
|
||||
.num_dacs = 8, /* DAC1 - analog out
|
||||
DAC2 - analog in monitor
|
||||
DAC3 - digital out monitor
|
||||
DAC4 - digital in monitor
|
||||
*/
|
||||
.ops = {
|
||||
.lock = juli_akm_lock,
|
||||
.unlock = juli_akm_unlock,
|
||||
.write = juli_akm_write,
|
||||
.set_rate_val = juli_akm_set_rate_val
|
||||
}
|
||||
},
|
||||
.dac_info = juli_dac,
|
||||
};
|
||||
|
||||
#define juli_mute_info snd_ctl_boolean_mono_info
|
||||
|
||||
static int juli_mute_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
val = ice->gpio.get_data(ice) & (unsigned int) kcontrol->private_value;
|
||||
if (kcontrol->private_value == GPIO_MUTE_CONTROL)
|
||||
/* val 0 = signal on */
|
||||
ucontrol->value.integer.value[0] = (val) ? 0 : 1;
|
||||
else
|
||||
/* val 1 = signal on */
|
||||
ucontrol->value.integer.value[0] = (val) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int juli_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int old_gpio, new_gpio;
|
||||
old_gpio = ice->gpio.get_data(ice);
|
||||
if (ucontrol->value.integer.value[0]) {
|
||||
/* unmute */
|
||||
if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
|
||||
/* 0 = signal on */
|
||||
new_gpio = old_gpio & ~GPIO_MUTE_CONTROL;
|
||||
/* un-smuting DAC */
|
||||
snd_akm4xxx_write(ice->akm, 0, 0x01, 0x01);
|
||||
} else
|
||||
/* 1 = signal on */
|
||||
new_gpio = old_gpio |
|
||||
(unsigned int) kcontrol->private_value;
|
||||
} else {
|
||||
/* mute */
|
||||
if (kcontrol->private_value == GPIO_MUTE_CONTROL) {
|
||||
/* 1 = signal off */
|
||||
new_gpio = old_gpio | GPIO_MUTE_CONTROL;
|
||||
/* smuting DAC */
|
||||
snd_akm4xxx_write(ice->akm, 0, 0x01, 0x03);
|
||||
} else
|
||||
/* 0 = signal off */
|
||||
new_gpio = old_gpio &
|
||||
~((unsigned int) kcontrol->private_value);
|
||||
}
|
||||
/* printk("JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, \
|
||||
new_gpio 0x%x\n",
|
||||
(unsigned int)ucontrol->value.integer.value[0], old_gpio,
|
||||
new_gpio); */
|
||||
if (old_gpio != new_gpio) {
|
||||
ice->gpio.set_data(ice, new_gpio);
|
||||
return 1;
|
||||
}
|
||||
/* no change */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Master Playback Switch",
|
||||
.info = juli_mute_info,
|
||||
.get = juli_mute_get,
|
||||
.put = juli_mute_put,
|
||||
.private_value = GPIO_MUTE_CONTROL,
|
||||
},
|
||||
/* Although the following functionality respects the succint NDA'd
|
||||
* documentation from the card manufacturer, and the same way of
|
||||
* operation is coded in OSS Juli driver, only Digital Out monitor
|
||||
* seems to work. Surprisingly, Analog input monitor outputs Digital
|
||||
* output data. The two are independent, as enabling both doubles
|
||||
* volume of the monitor sound.
|
||||
*
|
||||
* Checking traces on the board suggests the functionality described
|
||||
* by the manufacturer is correct - I2S from ADC and AK4114
|
||||
* go to ICE as well as to Xilinx, I2S inputs of DAC2,3,4 (the monitor
|
||||
* inputs) are fed from Xilinx.
|
||||
*
|
||||
* I even checked traces on board and coded a support in driver for
|
||||
* an alternative possiblity - the unused I2S ICE output channels
|
||||
* switched to HW-IN/SPDIF-IN and providing the monitoring signal to
|
||||
* the DAC - to no avail. The I2S outputs seem to be unconnected.
|
||||
*
|
||||
* The windows driver supports the monitoring correctly.
|
||||
*/
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Monitor Analog In Switch",
|
||||
.info = juli_mute_info,
|
||||
.get = juli_mute_get,
|
||||
.put = juli_mute_put,
|
||||
.private_value = GPIO_ANAIN_MONITOR,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Monitor Digital Out Switch",
|
||||
.info = juli_mute_info,
|
||||
.get = juli_mute_get,
|
||||
.put = juli_mute_put,
|
||||
.private_value = GPIO_DIGOUT_MONITOR,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Monitor Digital In Switch",
|
||||
.info = juli_mute_info,
|
||||
.get = juli_mute_get,
|
||||
.put = juli_mute_put,
|
||||
.private_value = GPIO_DIGIN_MONITOR,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void ak4358_proc_regs_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
|
||||
int reg, val;
|
||||
for (reg = 0; reg <= 0xf; reg++) {
|
||||
val = snd_akm4xxx_get(ice->akm, 0, reg);
|
||||
snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void ak4358_proc_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry))
|
||||
snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read);
|
||||
}
|
||||
|
||||
static char *slave_vols[] __devinitdata = {
|
||||
PCM_VOLUME,
|
||||
MONITOR_AN_IN_VOLUME,
|
||||
MONITOR_DIG_IN_VOLUME,
|
||||
MONITOR_DIG_OUT_VOLUME,
|
||||
NULL
|
||||
};
|
||||
|
||||
static __devinitdata
|
||||
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
|
||||
|
||||
static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
|
||||
const char *name)
|
||||
{
|
||||
struct snd_ctl_elem_id sid;
|
||||
memset(&sid, 0, sizeof(sid));
|
||||
/* FIXME: strcpy is bad. */
|
||||
strcpy(sid.name, name);
|
||||
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
return snd_ctl_find_id(card, &sid);
|
||||
}
|
||||
|
||||
static void __devinit add_slaves(struct snd_card *card,
|
||||
struct snd_kcontrol *master, char **list)
|
||||
{
|
||||
for (; *list; list++) {
|
||||
struct snd_kcontrol *slave = ctl_find(card, *list);
|
||||
/* printk(KERN_DEBUG "add_slaves - %s\n", *list); */
|
||||
if (slave) {
|
||||
/* printk(KERN_DEBUG "slave %s found\n", *list); */
|
||||
snd_ctl_add_slave(master, slave);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit juli_add_controls(struct snd_ice1712 *ice)
|
||||
{
|
||||
struct juli_spec *spec = ice->spec;
|
||||
int err;
|
||||
unsigned int i;
|
||||
struct snd_kcontrol *vmaster;
|
||||
|
||||
err = snd_ice1712_akm4xxx_build_controls(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(juli_mute_controls); i++) {
|
||||
err = snd_ctl_add(ice->card,
|
||||
snd_ctl_new1(&juli_mute_controls[i], ice));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
/* Create virtual master control */
|
||||
vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
|
||||
juli_master_db_scale);
|
||||
if (!vmaster)
|
||||
return -ENOMEM;
|
||||
add_slaves(ice->card, vmaster, slave_vols);
|
||||
err = snd_ctl_add(ice->card, vmaster);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* only capture SPDIF over AK4114 */
|
||||
err = snd_ak4114_build(spec->ak4114, NULL,
|
||||
ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
|
||||
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
|
||||
|
||||
ak4358_proc_init(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
@ -158,6 +500,74 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
|
|||
/*
|
||||
* initialize the chip
|
||||
*/
|
||||
|
||||
static inline int juli_is_spdif_master(struct snd_ice1712 *ice)
|
||||
{
|
||||
return (ice->gpio.get_data(ice) & GPIO_INTERNAL_CLOCK) ? 0 : 1;
|
||||
}
|
||||
|
||||
static unsigned int juli_get_rate(struct snd_ice1712 *ice)
|
||||
{
|
||||
int i;
|
||||
unsigned char result;
|
||||
|
||||
result = ice->gpio.get_data(ice) & GPIO_RATE_MASK;
|
||||
for (i = 0; i < ARRAY_SIZE(gpio_vals); i++)
|
||||
if (gpio_vals[i] == result)
|
||||
return juli_rates[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setting new rate */
|
||||
static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
|
||||
{
|
||||
unsigned int old, new;
|
||||
unsigned char val;
|
||||
|
||||
old = ice->gpio.get_data(ice);
|
||||
new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate);
|
||||
/* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n",
|
||||
old & GPIO_RATE_MASK,
|
||||
new & GPIO_RATE_MASK); */
|
||||
|
||||
ice->gpio.set_data(ice, new);
|
||||
/* switching to external clock - supplied by external circuits */
|
||||
val = inb(ICEMT1724(ice, RATE));
|
||||
outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
|
||||
}
|
||||
|
||||
static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
|
||||
unsigned int rate)
|
||||
{
|
||||
/* no change in master clock */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* setting clock to external - SPDIF */
|
||||
static void juli_set_spdif_clock(struct snd_ice1712 *ice)
|
||||
{
|
||||
unsigned int old;
|
||||
old = ice->gpio.get_data(ice);
|
||||
/* external clock (= 0), multiply 1x, 48kHz */
|
||||
ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
|
||||
GPIO_FREQ_48KHZ);
|
||||
}
|
||||
|
||||
/* Called when ak4114 detects change in the input SPDIF stream */
|
||||
static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
|
||||
unsigned char c1)
|
||||
{
|
||||
struct snd_ice1712 *ice = ak4114->change_callback_private;
|
||||
int rate;
|
||||
if (ice->is_spdif_master(ice) && c1) {
|
||||
/* only for SPDIF master mode, rate was changed */
|
||||
rate = snd_ak4114_external_rate(ak4114);
|
||||
/* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n",
|
||||
rate); */
|
||||
juli_akm_set_rate_val(ice->akm, rate);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit juli_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
static const unsigned char ak4114_init_vals[] = {
|
||||
|
@ -187,6 +597,11 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
|
|||
ice, &spec->ak4114);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* callback for codecs rate setting */
|
||||
spec->ak4114->change_callback = juli_ak4114_change;
|
||||
spec->ak4114->change_callback_private = ice;
|
||||
/* AK4114 in Juli can detect external rate correctly */
|
||||
spec->ak4114->check_flags = 0;
|
||||
|
||||
#if 0
|
||||
/* it seems that the analog doughter board detection does not work
|
||||
|
@ -210,6 +625,15 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* juli is clocked by Xilinx array */
|
||||
ice->hw_rates = &juli_rates_info;
|
||||
ice->is_spdif_master = juli_is_spdif_master;
|
||||
ice->get_rate = juli_get_rate;
|
||||
ice->set_rate = juli_set_rate;
|
||||
ice->set_mclk = juli_set_mclk;
|
||||
ice->set_spdif_clock = juli_set_spdif_clock;
|
||||
|
||||
ice->spdif.ops.open = juli_spdif_in_open;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -220,18 +644,20 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
|
|||
*/
|
||||
|
||||
static unsigned char juli_eeprom[] __devinitdata = {
|
||||
[ICE_EEP2_SYSCONF] = 0x20, /* clock 512, mpu401, 1xADC, 1xDACs */
|
||||
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
|
||||
SPDIF in */
|
||||
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
|
||||
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
|
||||
[ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */
|
||||
[ICE_EEP2_GPIO_DIR] = 0x9f,
|
||||
[ICE_EEP2_GPIO_DIR] = 0x9f, /* 5, 6:inputs; 7, 4-0 outputs*/
|
||||
[ICE_EEP2_GPIO_DIR1] = 0xff,
|
||||
[ICE_EEP2_GPIO_DIR2] = 0x7f,
|
||||
[ICE_EEP2_GPIO_MASK] = 0x9f,
|
||||
[ICE_EEP2_GPIO_MASK1] = 0xff,
|
||||
[ICE_EEP2_GPIO_MASK] = 0x60, /* 5, 6: locked; 7, 4-0 writable */
|
||||
[ICE_EEP2_GPIO_MASK1] = 0x00, /* 0-7 writable */
|
||||
[ICE_EEP2_GPIO_MASK2] = 0x7f,
|
||||
[ICE_EEP2_GPIO_STATE] = 0x16, /* internal clock, multiple 1x, 48kHz */
|
||||
[ICE_EEP2_GPIO_STATE1] = 0x80, /* mute */
|
||||
[ICE_EEP2_GPIO_STATE] = GPIO_FREQ_48KHZ | GPIO_MULTI_1X |
|
||||
GPIO_INTERNAL_CLOCK, /* internal clock, multiple 1x, 48kHz*/
|
||||
[ICE_EEP2_GPIO_STATE1] = 0x00, /* unmuted */
|
||||
[ICE_EEP2_GPIO_STATE2] = 0x00,
|
||||
};
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
|
|||
wm_put(ice, WM_ADC_MUX, nval);
|
||||
}
|
||||
mutex_unlock(&ice->gpio_mutex);
|
||||
return 0;
|
||||
return change;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -450,7 +450,7 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
|
|||
change = 1;
|
||||
}
|
||||
mutex_unlock(&ice->gpio_mutex);
|
||||
return 0;
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -319,12 +319,11 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
|
|||
/*
|
||||
* Handler for setting correct codec rate - called when rate change is detected
|
||||
*/
|
||||
static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
|
||||
static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
|
||||
{
|
||||
unsigned char old, new;
|
||||
int idx;
|
||||
unsigned char changed[7];
|
||||
struct snd_ice1712 *ice = ak->private_data[0];
|
||||
struct prodigy192_spec *spec = ice->spec;
|
||||
|
||||
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
|
||||
|
@ -357,16 +356,6 @@ static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
|
|||
mutex_unlock(&spec->mute_mutex);
|
||||
}
|
||||
|
||||
/* using akm infrastructure for setting rate of the codec */
|
||||
static struct snd_akm4xxx akmlike_stac9460 __devinitdata = {
|
||||
.type = NON_AKM, /* special value */
|
||||
.num_adcs = 6, /* not used in any way, just for completeness */
|
||||
.num_dacs = 2,
|
||||
.ops = {
|
||||
.set_rate_val = stac9460_set_rate_val
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
|
||||
|
@ -642,12 +631,19 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
|
|||
0x41, 0x02, 0x2c, 0x00, 0x00
|
||||
};
|
||||
struct prodigy192_spec *spec = ice->spec;
|
||||
int err;
|
||||
|
||||
return snd_ak4114_create(ice->card,
|
||||
err = snd_ak4114_create(ice->card,
|
||||
prodigy192_ak4114_read,
|
||||
prodigy192_ak4114_write,
|
||||
ak4114_init_vals, ak4114_init_txcsb,
|
||||
ice, &spec->ak4114);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* AK4114 in Prodigy192 cannot detect external rate correctly.
|
||||
* No reason to stop capture stream due to incorrect checks */
|
||||
spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stac9460_proc_regs_read(struct snd_info_entry *entry,
|
||||
|
@ -743,7 +739,6 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
|
|||
};
|
||||
const unsigned short *p;
|
||||
int err = 0;
|
||||
struct snd_akm4xxx *ak;
|
||||
struct prodigy192_spec *spec;
|
||||
|
||||
/* prodigy 192 */
|
||||
|
@ -761,15 +756,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
|
|||
p = stac_inits_prodigy;
|
||||
for (; *p != (unsigned short)-1; p += 2)
|
||||
stac9460_put(ice, p[0], p[1]);
|
||||
/* reusing the akm codecs infrastructure,
|
||||
* for setting rate on stac9460 */
|
||||
ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
|
||||
if (!ak)
|
||||
return -ENOMEM;
|
||||
ice->akm_codecs = 1;
|
||||
err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ice->gpio.set_pro_rate = stac9460_set_rate_val;
|
||||
|
||||
/* MI/ODI/O add on card with AK4114 */
|
||||
if (prodigy192_miodio_exists(ice)) {
|
||||
|
@ -825,10 +812,6 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
|
|||
.build_controls = prodigy192_add_controls,
|
||||
.eeprom_size = sizeof(prodigy71_eeprom),
|
||||
.eeprom_data = prodigy71_eeprom,
|
||||
/* the current MPU401 code loops infinitely
|
||||
* when opening midi device
|
||||
*/
|
||||
.no_mpu401 = 1,
|
||||
},
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
|
|
@ -322,17 +322,23 @@ static struct snd_pt2258 ptc_revo51_volume;
|
|||
static void ap192_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
|
||||
{
|
||||
struct snd_ice1712 *ice = ak->private_data[0];
|
||||
int dfs;
|
||||
|
||||
revo_set_rate_val(ak, rate);
|
||||
|
||||
#if 1 /* FIXME: do we need this procedure? */
|
||||
/* reset DFS pin of AK5385A for ADC, too */
|
||||
/* DFS0 (pin 18) -- GPIO10 pin 77 */
|
||||
snd_ice1712_save_gpio_status(ice);
|
||||
snd_ice1712_gpio_write_bits(ice, 1 << 10,
|
||||
rate > 48000 ? (1 << 10) : 0);
|
||||
snd_ice1712_restore_gpio_status(ice);
|
||||
#endif
|
||||
/* reset CKS */
|
||||
snd_ice1712_gpio_write_bits(ice, 1 << 8, rate > 96000 ? 1 << 8 : 0);
|
||||
/* reset DFS pins of AK5385A for ADC, too */
|
||||
if (rate > 96000)
|
||||
dfs = 2;
|
||||
else if (rate > 48000)
|
||||
dfs = 1;
|
||||
else
|
||||
dfs = 0;
|
||||
snd_ice1712_gpio_write_bits(ice, 3 << 9, dfs << 9);
|
||||
/* reset ADC */
|
||||
snd_ice1712_gpio_write_bits(ice, 1 << 11, 0);
|
||||
snd_ice1712_gpio_write_bits(ice, 1 << 11, 1 << 11);
|
||||
}
|
||||
|
||||
static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
|
||||
|
@ -353,28 +359,20 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
|
|||
.cif = 0,
|
||||
.data_mask = VT1724_REVO_CDOUT,
|
||||
.clk_mask = VT1724_REVO_CCLK,
|
||||
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
|
||||
.cs_addr = VT1724_REVO_CS3,
|
||||
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
|
||||
.cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
|
||||
.cs_addr = VT1724_REVO_CS1,
|
||||
.cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
|
||||
.add_flags = VT1724_REVO_CCLK, /* high at init */
|
||||
.mask_flags = 0,
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* FIXME: ak4114 makes the sound much lower due to some confliction,
|
||||
* so let's disable it right now...
|
||||
*/
|
||||
#define BUILD_AK4114_AP192
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_AK4114_AP192
|
||||
/* AK4114 support on Audiophile 192 */
|
||||
/* CDTO (pin 32) -- GPIO2 pin 52
|
||||
* CDTI (pin 33) -- GPIO3 pin 53 (shared with AK4358)
|
||||
* CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
|
||||
* CSN (pin 35) -- GPIO7 pin 59
|
||||
*/
|
||||
#define AK4114_ADDR 0x00
|
||||
#define AK4114_ADDR 0x02
|
||||
|
||||
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
|
||||
unsigned int data, int idx)
|
||||
|
@ -428,7 +426,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
|
|||
tmp = snd_ice1712_gpio_read(ice);
|
||||
tmp |= VT1724_REVO_CCLK; /* high at init */
|
||||
tmp |= VT1724_REVO_CS0;
|
||||
tmp &= ~VT1724_REVO_CS3;
|
||||
tmp &= ~VT1724_REVO_CS1;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(1);
|
||||
return tmp;
|
||||
|
@ -436,7 +434,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
|
|||
|
||||
static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
|
||||
{
|
||||
tmp |= VT1724_REVO_CS3;
|
||||
tmp |= VT1724_REVO_CS1;
|
||||
tmp |= VT1724_REVO_CS0;
|
||||
snd_ice1712_gpio_write(ice, tmp);
|
||||
udelay(1);
|
||||
|
@ -485,13 +483,17 @@ static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice)
|
|||
struct ak4114 *ak;
|
||||
int err;
|
||||
|
||||
return snd_ak4114_create(ice->card,
|
||||
err = snd_ak4114_create(ice->card,
|
||||
ap192_ak4114_read,
|
||||
ap192_ak4114_write,
|
||||
ak4114_init_vals, ak4114_init_txcsb,
|
||||
ice, &ak);
|
||||
/* AK4114 in Revo cannot detect external rate correctly.
|
||||
* No reason to stop capture stream due to incorrect checks */
|
||||
ak->check_flags = AK4114_CHECK_NO_RATE;
|
||||
|
||||
return 0; /* error ignored; it's no fatal error */
|
||||
}
|
||||
#endif /* BUILD_AK4114_AP192 */
|
||||
|
||||
static int __devinit revo_init(struct snd_ice1712 *ice)
|
||||
{
|
||||
|
@ -557,6 +559,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* unmute all codecs */
|
||||
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
|
||||
VT1724_REVO_MUTE);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -588,11 +593,9 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
|
|||
err = snd_ice1712_akm4xxx_build_controls(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
#ifdef BUILD_AK4114_AP192
|
||||
err = ap192_ak4114_init(ice);
|
||||
if (err < 0)
|
||||
return err;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -155,7 +155,8 @@ DEFINE_REGSET(SP, 0x60); /* SPDIF out */
|
|||
#define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */
|
||||
#define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */
|
||||
#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */
|
||||
#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */
|
||||
#define ICH_PCM_246_MASK 0x00300000 /* chan mask (not all chips) */
|
||||
#define ICH_PCM_8 0x00300000 /* 8 channels (not all chips) */
|
||||
#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */
|
||||
#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */
|
||||
#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */
|
||||
|
@ -382,6 +383,7 @@ struct intel8x0 {
|
|||
|
||||
unsigned multi4: 1,
|
||||
multi6: 1,
|
||||
multi8 :1,
|
||||
dra: 1,
|
||||
smp20bit: 1;
|
||||
unsigned in_ac97_init: 1,
|
||||
|
@ -997,6 +999,8 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
|
|||
cnt |= ICH_PCM_4;
|
||||
else if (runtime->channels == 6)
|
||||
cnt |= ICH_PCM_6;
|
||||
else if (runtime->channels == 8)
|
||||
cnt |= ICH_PCM_8;
|
||||
if (chip->device_type == DEVICE_NFORCE) {
|
||||
/* reset to 2ch once to keep the 6 channel data in alignment,
|
||||
* to start from Front Left always
|
||||
|
@ -1106,6 +1110,16 @@ static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
|
|||
.mask = 0,
|
||||
};
|
||||
|
||||
static unsigned int channels8[] = {
|
||||
2, 4, 6, 8,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
|
||||
.count = ARRAY_SIZE(channels8),
|
||||
.list = channels8,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)
|
||||
{
|
||||
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
|
||||
|
@ -1136,7 +1150,12 @@ static int snd_intel8x0_playback_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (chip->multi6) {
|
||||
if (chip->multi8) {
|
||||
runtime->hw.channels_max = 8;
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&hw_constraints_channels8);
|
||||
} else if (chip->multi6) {
|
||||
runtime->hw.channels_max = 6;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
&hw_constraints_channels6);
|
||||
|
@ -2203,8 +2222,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
|
|||
}
|
||||
if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
|
||||
chip->multi4 = 1;
|
||||
if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE))
|
||||
if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) {
|
||||
chip->multi6 = 1;
|
||||
if (chip->ac97[0]->flags & AC97_HAS_8CH)
|
||||
chip->multi8 = 1;
|
||||
}
|
||||
}
|
||||
if (pbus->pcms[0].r[1].rslots[0]) {
|
||||
chip->dra = 1;
|
||||
|
@ -2446,7 +2468,7 @@ static int snd_intel8x0_free(struct intel8x0 *chip)
|
|||
pci_write_config_dword(chip->pci, 0x4c, val);
|
||||
}
|
||||
/* --- */
|
||||
synchronize_irq(chip->irq);
|
||||
|
||||
__hw_end:
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
|
@ -2495,7 +2517,6 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
chip->sdm_saved = igetbyte(chip, ICHREG(SDM));
|
||||
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
}
|
||||
|
@ -2648,7 +2669,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
|
|||
t = stop_time.tv_sec - start_time.tv_sec;
|
||||
t *= 1000000;
|
||||
t += stop_time.tv_usec - start_time.tv_usec;
|
||||
printk(KERN_INFO "%s: measured %lu usecs\n", __FUNCTION__, t);
|
||||
printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t);
|
||||
if (t == 0) {
|
||||
snd_printk(KERN_ERR "?? calculation error..\n");
|
||||
return;
|
||||
|
|
|
@ -985,17 +985,15 @@ static int snd_intel8x0_free(struct intel8x0m *chip)
|
|||
/* reset channels */
|
||||
for (i = 0; i < chip->bdbars_count; i++)
|
||||
iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
|
||||
/* --- */
|
||||
synchronize_irq(chip->irq);
|
||||
__hw_end:
|
||||
__hw_end:
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
if (chip->bdbars.area)
|
||||
snd_dma_free_pages(&chip->bdbars);
|
||||
if (chip->addr)
|
||||
pci_iounmap(chip->pci, chip->addr);
|
||||
if (chip->bmaddr)
|
||||
pci_iounmap(chip->pci, chip->bmaddr);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
kfree(chip);
|
||||
|
@ -1017,7 +1015,6 @@ static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state)
|
|||
snd_pcm_suspend_all(chip->pcm[i]);
|
||||
snd_ac97_suspend(chip->ac97);
|
||||
if (chip->irq >= 0) {
|
||||
synchronize_irq(chip->irq);
|
||||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче