dect
/
linux-2.6
Archived
13
0
Fork 0

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:
Linus Torvalds 2008-04-24 08:41:44 -07:00
commit 38ccc197eb
170 changed files with 10717 additions and 2299 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

235
sound/drivers/pcsp/pcsp.c Normal file
View File

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

82
sound/drivers/pcsp/pcsp.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
au88x0/ \
aw2/ \
ca0106/ \
cs46xx/ \
cs5535audio/ \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

3
sound/pci/aw2/Makefile Normal file
View File

@ -0,0 +1,3 @@
snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
obj-$(CONFIG_SND_AW2) += snd-aw2.o

794
sound/pci/aw2/aw2-alsa.c Normal file
View File

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

465
sound/pci/aw2/aw2-saa7146.c Normal file
View File

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

105
sound/pci/aw2/aw2-saa7146.h Normal file
View File

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

110
sound/pci/aw2/aw2-tsl.c Normal file
View File

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

168
sound/pci/aw2/saa7146.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More