diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-24 08:41:44 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-24 08:41:44 -0700 |
commit | 38ccc197eb85cad594eb5863ba5a408655da0062 (patch) | |
tree | 0066a0160e5dd28280a8db2a5814af7111ae3e1b /sound | |
parent | d02aacff4467806ee56f147ac8eff6911d95811a (diff) | |
parent | 3a841d519f91463361bbbe7addc24a0c1b2e9f99 (diff) |
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
...
Diffstat (limited to 'sound')
157 files changed, 10614 insertions, 2274 deletions
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 8704e2825b1..5b3274b465e 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -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"); diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 829ca38b595..a8d71c6c8e7 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -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 diff --git a/sound/core/Makefile b/sound/core/Makefile index 267039a97bd..da8e685eef9 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -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 diff --git a/sound/core/init.c b/sound/core/init.c index e3338d6071e..ac057341613 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -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; diff --git a/sound/core/misc.c b/sound/core/misc.c index 102d1c36cf2..38524f615d9 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -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); diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 75daed298a1..581aa2c60e6 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -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 }, diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index ab570a0a618..558dadbf45f 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -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)); diff --git a/sound/pci/hda/vmaster.c b/sound/core/vmaster.c index 2da49d20a1f..4cc57f902e2 100644 --- a/sound/pci/hda/vmaster.c +++ b/sound/core/vmaster.c @@ -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); diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 75d4fe09fdf..fe85af1c569 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -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 diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index 8e5530006e1..d4a07f9ff2c 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -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/ diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index a240eaeb5c6..4e4c69e6cb4 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -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 = diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index 05a871aa7b8..ecdbeb6d360 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -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, }, }; diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 5993864acbd..18cca2457d4 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -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); diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile new file mode 100644 index 00000000000..b19555b440d --- /dev/null +++ b/sound/drivers/pcsp/Makefile @@ -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 diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c new file mode 100644 index 00000000000..59203511e77 --- /dev/null +++ b/sound/drivers/pcsp/pcsp.c @@ -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); diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h new file mode 100644 index 00000000000..f07cc1ee1fe --- /dev/null +++ b/sound/drivers/pcsp/pcsp.h @@ -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 diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c new file mode 100644 index 00000000000..cd9b83e7f7d --- /dev/null +++ b/sound/drivers/pcsp/pcsp_input.c @@ -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; +} diff --git a/sound/drivers/pcsp/pcsp_input.h b/sound/drivers/pcsp/pcsp_input.h new file mode 100644 index 00000000000..e66738c7833 --- /dev/null +++ b/sound/drivers/pcsp/pcsp_input.h @@ -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 diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c new file mode 100644 index 00000000000..ac6238e9351 --- /dev/null +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -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; +} diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c new file mode 100644 index 00000000000..64a695fef74 --- /dev/null +++ b/sound/drivers/pcsp/pcsp_mixer.c @@ -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; +} diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index 15061bd7277..d20d893b3b6 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -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); } diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 35fbbf2cb9f..288926d2e20 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -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; } diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index bed29ca2223..f3fd7b4f466 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -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; diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index d63c1af550d..b432d9ae874 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -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; } diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index eaf69971bf9..1e90d769b62 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -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 diff --git a/sound/oss/trident.c b/sound/oss/trident.c index d6af9065d1c..f43f91ef86c 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -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; diff --git a/sound/oss/trident.h b/sound/oss/trident.h index 4713b49fc91..ff30a1d7c2f 100644 --- a/sound/oss/trident.h +++ b/sound/oss/trident.h @@ -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; \ } \ }) diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c index d25249a932b..2c5aaa58046 100644 --- a/sound/oss/vwsnd.c +++ b/sound/oss/vwsnd.c @@ -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)) diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 812085d521f..581debf37dc 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -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. diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 2d42fd28f4e..85ef14bc805 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SND) += \ ac97/ \ ali5451/ \ au88x0/ \ + aw2/ \ ca0106/ \ cs46xx/ \ cs5535audio/ \ diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 50c637e55ff..39198e505b1 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -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 diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 3674f35c4a7..48cbda9378c 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -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); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index a66d5150bb7..39ec55b57b1 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -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); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 6a905ed9cbd..1a0fd65ec28 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -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); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 0e990a73582..8df6824b51c 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -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() diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 4594186b83e..457228fb22a 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -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); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index a67a869180d..d457a32a793 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -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); diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 26819e2f576..68368e49007 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -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); diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 526c6c5ecf7..f9a58b4a30e 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -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; diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile new file mode 100644 index 00000000000..842335d3b73 --- /dev/null +++ b/sound/pci/aw2/Makefile @@ -0,0 +1,3 @@ +snd-aw2-objs := aw2-alsa.o aw2-saa7146.o + +obj-$(CONFIG_SND_AW2) += snd-aw2.o diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c new file mode 100644 index 00000000000..56f87cd33c1 --- /dev/null +++ b/sound/pci/aw2/aw2-alsa.c @@ -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; +} diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c new file mode 100644 index 00000000000..6a3891ab69d --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.c @@ -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; +} diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h new file mode 100644 index 00000000000..5b35e358937 --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.h @@ -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 diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c new file mode 100644 index 00000000000..459b0311ea3 --- /dev/null +++ b/sound/pci/aw2/aw2-tsl.c @@ -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 +}; diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h new file mode 100644 index 00000000000..ce0ab5f9ee9 --- /dev/null +++ b/sound/pci/aw2/saa7146.h @@ -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 diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 4e71a55120a..5f63af6b88a 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -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) diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 176e0f0e805..ecbe79b67e4 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -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; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index af736869d9b..3025ed1b6e1 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -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; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 135f3086075..9971b5b7735 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -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); } diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 87ddffcd9d8..e214e567dec 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -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); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 90ec090792b..e16dc92e82f 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -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); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 9a9b977d3cf..abde5b90188 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -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 */ diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 5512abd98bd..491a4a50f86 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -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 | diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index f3caa3f890c..216f9748aff 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -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"); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 72d85a5ae6a..fbf1124f7c7 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -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 diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 1a314fa99c4..84fac1fbf10 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -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); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 7d911a18c08..1bf298d214b 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -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 */ } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 4c300e6149f..c129f9e2072 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -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; } } diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 9e0d8a1268a..ab0c726d648 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -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 diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 37c413923db..a6be6e3e871 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -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) @@ -1512,6 +1576,43 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) } /* + * 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) -{ - substream->runtime->hw.channels_max = mout->max_channels; + struct snd_pcm_substream *substream, + struct hda_pcm_stream *hinfo) +{ + 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); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f14871151be..dcd390b2bba 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -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, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f9de7c467c2..59e4389c94a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -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; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4be36c84b36..b3a618eb42c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -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; - 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; - } - err = create_codec_pcm(chip, codec, - &codec->pcm_info[c], pcm_dev); - if (err < 0) - return err; - pcm_dev++; - } - } - - /* create modem PCMs */ - pcm_dev = AZX_MAX_AUDIO_PCMS; + 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; /* already created */ - if (pcm_dev >= AZX_MAX_PCMS) { - snd_printk(KERN_ERR SFX - "Too many modem 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; - 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); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ad0014ab71f..5c9e578f7f2 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -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 */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index f5c23bb16d7..2fdf2358dbc 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -18,31 +18,3 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[]; extern struct hda_codec_preset snd_hda_preset_conexant[]; /* VIA codecs */ extern struct hda_codec_preset snd_hda_preset_via[]; - -static const struct hda_codec_preset *hda_preset_tables[] = { -#ifdef CONFIG_SND_HDA_CODEC_REALTEK - snd_hda_preset_realtek, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CMEDIA - snd_hda_preset_cmedia, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ANALOG - snd_hda_preset_analog, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL - snd_hda_preset_sigmatel, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SI3054 - snd_hda_preset_si3054, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI - snd_hda_preset_atihdmi, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CONEXANT - snd_hda_preset_conexant, -#endif -#ifdef CONFIG_SND_HDA_CODEC_VIA - snd_hda_preset_via, -#endif - NULL -}; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index c8649282c2c..e0a605adde4 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -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; } @@ -3356,6 +3431,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 * * port-A - front hp-out @@ -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 */ }; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index 9a8bb4ce3f8..12272508b11 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -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 */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 3d6097ba1d6..c73ce074a6e 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -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 */ }; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7206b30cbf9..36fd8526003 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -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, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 33282f9c01c..cdda64b02f4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -30,6 +30,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_patch.h" #define ALC880_FRONT_EVENT 0x01 #define ALC880_DCVOL_EVENT 0x02 @@ -97,16 +98,19 @@ enum { ALC262_SONY_ASSAMD, ALC262_BENQ_T31, ALC262_ULTRA, + ALC262_LENOVO_3000, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; /* ALC268 models */ enum { + ALC267_QUANTA_IL1, ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, ALC268_DELL, + ALC268_ZEPTO, #ifdef CONFIG_SND_DEBUG ALC268_TEST, #endif @@ -195,10 +199,11 @@ enum { ALC883_LENOVO_NB0763, ALC888_LENOVO_MS7195_DIG, ALC883_HAIER_W66, - ALC888_6ST_HP, ALC888_3ST_HP, ALC888_6ST_DELL, ALC883_MITAC, + ALC883_CLEVO_M720, + ALC883_FUJITSU_PI2515, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -237,6 +242,7 @@ struct alc_spec { /* capture */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ /* capture source */ @@ -270,7 +276,6 @@ struct alc_spec { /* for virtual master */ hda_nid_t vmaster_nid; - u32 vmaster_tlv[4]; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -290,6 +295,7 @@ struct alc_config_preset { hda_nid_t hp_nid; /* optional */ unsigned int num_adc_nids; hda_nid_t *adc_nids; + hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; unsigned int num_channel_mode; const struct hda_channel_mode *channel_mode; @@ -336,9 +342,10 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - spec->adc_nids[adc_idx], - &spec->cur_mux[adc_idx]); + nid, &spec->cur_mux[adc_idx]); } @@ -707,6 +714,7 @@ static void setup_preset(struct alc_spec *spec, spec->num_adc_nids = preset->num_adc_nids; spec->adc_nids = preset->adc_nids; + spec->capsrc_nids = preset->capsrc_nids; spec->dig_in_nid = preset->dig_in_nid; spec->unsol_event = preset->unsol_event; @@ -741,7 +749,6 @@ static struct hda_verb alc_gpio3_init_verbs[] = { static void alc_sku_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int mute; unsigned int present; unsigned int hp_nid = spec->autocfg.hp_pins[0]; unsigned int sp_nid = spec->autocfg.speaker_pins[0]; @@ -751,16 +758,8 @@ static void alc_sku_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); - } + snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->jack_present ? 0 : PIN_OUT); } /* unsolicited event for HP jack sensing */ @@ -1319,11 +1318,19 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), { } /* end */ }; +static struct hda_input_mux alc880_f1734_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "CD", 0x4 }, + }, +}; + /* * ALC880 ASUS model @@ -1516,6 +1523,11 @@ static int alc_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); @@ -1525,10 +1537,11 @@ static int alc_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, alc_slave_vols); + vmaster_tlv, alc_slave_vols); if (err < 0) return err; } @@ -1882,7 +1895,7 @@ static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; bits = present ? HDA_AMP_MUTE : 0; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_INPUT, 0, HDA_AMP_MUTE, bits); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); } static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) @@ -1915,6 +1928,7 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, * HP = 0x14, speaker-out = 0x15, mic = 0x18 */ static struct hda_verb alc880_pin_f1734_init_verbs[] = { + {0x07, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -1927,7 +1941,7 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -1935,6 +1949,9 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT}, + { } }; @@ -2318,7 +2335,8 @@ static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct alc_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 alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -2392,8 +2410,8 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - 0, 0, 0); + snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); return 0; } @@ -2498,6 +2516,7 @@ static int alc_build_pcms(struct hda_codec *codec) codec->num_pcms = 2; info = spec->pcm_rec + 1; info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->multiout.dig_out_nid && spec->stream_digital_playback) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback); @@ -2560,6 +2579,7 @@ static void alc_free(struct hda_codec *codec) kfree(spec->kctl_alloc); } kfree(spec); + codec->spec = NULL; /* to be sure */ } /* @@ -3057,7 +3077,9 @@ static struct alc_config_preset alc880_presets[] = { .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), .channel_mode = alc880_2_jack_modes, - .input_mux = &alc880_capture_source, + .input_mux = &alc880_f1734_capture_source, + .unsol_event = alc880_uniwill_p53_unsol_event, + .init_hook = alc880_uniwill_p53_hp_automute, }, [ALC880_ASUS] = { .mixers = { alc880_asus_mixer }, @@ -3467,15 +3489,21 @@ static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, return 0; } -static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t nid, int pin_type, - int dac_idx) +static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, + unsigned int pin_type) { - /* set as output */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + /* unmute pin */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); +} + +static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -3597,9 +3625,12 @@ static int alc880_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc880_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc880_auto_init_multi_out(codec); alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -4795,11 +4826,7 @@ static void alc260_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int sel_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (nid >= 0x12) { int idx = nid - 0x12; @@ -4929,7 +4956,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* check whether NID 0x04 is valid */ wcap = get_wcaps(codec, 0x04); wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc260_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; @@ -4946,8 +4973,11 @@ static int alc260_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc260_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc260_auto_init_multi_out(codec); alc260_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5204,6 +5234,9 @@ static hda_nid_t alc882_dac_nids[4] = { #define alc882_adc_nids alc880_adc_nids #define alc882_adc_nids_alt alc880_adc_nids_alt +static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 }; +static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -5226,15 +5259,11 @@ static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux = spec->input_mux; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; - hda_nid_t nid; + hda_nid_t nid = spec->capsrc_nids ? + spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; unsigned int *cur_val = &spec->cur_mux[adc_idx]; unsigned int i, idx; - if (spec->num_adc_nids < 3) - nid = capture_mixers[adc_idx + 1]; - else - nid = capture_mixers[adc_idx]; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; @@ -6111,6 +6140,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6127,6 +6157,7 @@ static struct alc_config_preset alc882_presets[] = { .dig_out_nid = ALC882_DIGOUT_NID, .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), .adc_nids = alc882_adc_nids, + .capsrc_nids = alc882_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes), .channel_mode = alc882_3ST_6ch_modes, .need_dac_fix = 1, @@ -6182,15 +6213,11 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; else idx = spec->multiout.dac_nids[dac_idx] - 2; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -6219,6 +6246,9 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc882_is_input_pin(nid) alc880_is_input_pin(nid) @@ -6231,16 +6261,21 @@ static void alc882_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = spec->autocfg.input_pins[i]; - if (alc882_is_input_pin(nid)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - i <= AUTO_PIN_FRONT_MIC ? - PIN_VREF80 : PIN_IN); - if (nid != ALC882_PIN_CD_NID) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); + unsigned int vref; + if (!nid) + continue; + vref = PIN_IN; + if (1 /*i <= AUTO_PIN_FRONT_MIC*/) { + if (snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) & + AC_PINCAP_VREF_80) + vref = PIN_VREF80; } + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, vref); + if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); } } @@ -6294,11 +6329,16 @@ static int alc882_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc882_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc882_auto_init_multi_out(codec); alc882_auto_init_hp_out(codec); alc882_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } +static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ + static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; @@ -6328,6 +6368,11 @@ static int patch_alc882(struct hda_codec *codec) board_config = ALC885_MBP3; break; default: + /* ALC889A is handled better as ALC888-compatible */ + if (codec->revision_id == 0x100103) { + alc_free(codec); + return patch_alc883(codec); + } printk(KERN_INFO "hda_codec: Unknown model for ALC882, " "trying auto-probe from BIOS...\n"); board_config = ALC882_AUTO; @@ -6372,12 +6417,14 @@ static int patch_alc882(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc882_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); + spec->capsrc_nids = alc882_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc882_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc882_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); + spec->capsrc_nids = alc882_capsrc_nids; spec->mixers[spec->num_mixers] = alc882_capture_mixer; spec->num_mixers++; } @@ -6412,7 +6459,7 @@ static int patch_alc882(struct hda_codec *codec) static hda_nid_t alc883_dac_nids[4] = { /* front, rear, clfe, rear_surr */ - 0x02, 0x04, 0x03, 0x05 + 0x02, 0x03, 0x04, 0x05 }; static hda_nid_t alc883_adc_nids[2] = { @@ -6420,6 +6467,8 @@ static hda_nid_t alc883_adc_nids[2] = { 0x08, 0x09, }; +static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -6451,35 +6500,18 @@ static struct hda_input_mux alc883_lenovo_nb0763_capture_source = { }, }; +static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "Int Mic", 0x1 }, + }, +}; + #define alc883_mux_enum_info alc_mux_enum_info #define alc883_mux_enum_get alc_mux_enum_get - -static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[2] = { 0x23, 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC883 has the ALC882-type input selection */ +#define alc883_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -6638,6 +6670,60 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6787,6 +6873,9 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), @@ -6878,124 +6967,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { { } /* end */ }; -static struct snd_kcontrol_new alc888_6st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc888_6st_dell_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, - { } /* end */ -}; - static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -7171,6 +7142,35 @@ static struct hda_verb alc883_mitac_verbs[] = { { } /* end */ }; +static struct hda_verb alc883_clevo_m720_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Int speaker */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = { + /* HP */ + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + static struct hda_verb alc883_tagra_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -7227,26 +7227,14 @@ static struct hda_verb alc883_haier_w66_verbs[] = { { } /* end */ }; -static struct hda_verb alc888_6st_hp_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ - { } -}; - static struct hda_verb alc888_3st_hp_verbs[] = { {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ { } }; static struct hda_verb alc888_6st_dell_verbs[] = { - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */ - {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } }; @@ -7354,6 +7342,68 @@ static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) alc883_tagra_automute(codec); } +/* toggle speaker-output according to the hp-jack state */ +static void alc883_clevo_m720_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_clevo_m720_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc883_clevo_m720_automute(struct hda_codec *codec) +{ + alc883_clevo_m720_hp_automute(codec); + alc883_clevo_m720_mic_automute(codec); +} + +static void alc883_clevo_m720_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc883_clevo_m720_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + alc883_clevo_m720_mic_automute(codec); + break; + } +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc883_2ch_fujitsu_pi2515_automute(codec); +} + static void alc883_haier_w66_automute(struct hda_codec *codec) { unsigned int present; @@ -7587,10 +7637,11 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_LENOVO_NB0763] = "lenovo-nb0763", [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", [ALC883_HAIER_W66] = "haier-w66", - [ALC888_6ST_HP] = "6stack-hp", [ALC888_3ST_HP] = "3stack-hp", [ALC888_6ST_DELL] = "6stack-dell", [ALC883_MITAC] = "mitac", + [ALC883_CLEVO_M720] = "clevo-m720", + [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", [ALC883_AUTO] = "auto", }; @@ -7604,7 +7655,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), @@ -7614,7 +7665,9 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), @@ -7627,13 +7680,17 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720), + SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720), SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), + SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), @@ -7652,8 +7709,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, @@ -7665,8 +7720,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, @@ -7678,8 +7731,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7691,8 +7742,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, @@ -7704,8 +7753,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7719,8 +7766,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7737,8 +7782,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7749,8 +7792,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7764,8 +7805,6 @@ static struct alc_config_preset alc883_presets[] = { alc883_medion_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, @@ -7776,8 +7815,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, @@ -7789,19 +7826,27 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, }, + [ALC883_CLEVO_M720] = { + .mixers = { alc883_clevo_m720_mixer }, + .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_clevo_m720_unsol_event, + .init_hook = alc883_clevo_m720_automute, + }, [ALC883_LENOVO_101E_2ch] = { .mixers = { alc883_lenovo_101e_2ch_mixer}, .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_lenovo_101e_capture_source, @@ -7813,8 +7858,6 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .need_dac_fix = 1, @@ -7828,8 +7871,6 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, @@ -7843,47 +7884,28 @@ static struct alc_config_preset alc883_presets[] = { .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_haier_w66_unsol_event, .init_hook = alc883_haier_w66_automute, - }, - [ALC888_6ST_HP] = { - .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer }, - .init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs }, - .num_dacs = ARRAY_SIZE(alc883_dac_nids), - .dac_nids = alc883_dac_nids, - .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, - .dig_in_nid = ALC883_DIGIN_NID, - .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), - .channel_mode = alc883_sixstack_modes, - .input_mux = &alc883_capture_source, }, [ALC888_3ST_HP] = { - .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer }, + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), .channel_mode = alc888_3st_hp_modes, .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, [ALC888_6ST_DELL] = { - .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer }, + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .dig_out_nid = ALC883_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .dig_in_nid = ALC883_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, @@ -7896,14 +7918,25 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), - .adc_nids = alc883_adc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, .unsol_event = alc883_mitac_unsol_event, .init_hook = alc883_mitac_automute, }, + [ALC883_FUJITSU_PI2515] = { + .mixers = { alc883_2ch_fujitsu_pi2515_mixer }, + .init_verbs = { alc883_init_verbs, + alc883_2ch_fujitsu_pi2515_verbs}, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_fujitsu_pi2515_capture_source, + .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, + .init_hook = alc883_2ch_fujitsu_pi2515_automute, + }, }; @@ -7918,15 +7951,11 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; int idx; + alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; else idx = spec->multiout.dac_nids[dac_idx] - 2; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } @@ -7955,6 +7984,9 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc883_is_input_pin(nid) alc880_is_input_pin(nid) @@ -8006,9 +8038,12 @@ static int alc883_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc883_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc883_auto_init_multi_out(codec); alc883_auto_init_hp_out(codec); alc883_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc883(struct hda_codec *codec) @@ -8057,10 +8092,9 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_capture = &alc883_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc883_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - } + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + spec->adc_nids = alc883_adc_nids; + spec->capsrc_nids = alc883_capsrc_nids; spec->vmaster_nid = 0x0c; @@ -8085,6 +8119,8 @@ static int patch_alc883(struct hda_codec *codec) #define alc262_dac_nids alc260_dac_nids #define alc262_adc_nids alc882_adc_nids #define alc262_adc_nids_alt alc882_adc_nids_alt +#define alc262_capsrc_nids alc882_capsrc_nids +#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt #define alc262_modes alc260_modes #define alc262_capture_source alc882_capture_source @@ -8585,7 +8621,8 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, /* * fujitsu model - * 0x14 = headphone/spdif-out, 0x15 = internal speaker + * 0x14 = headphone/spdif-out, 0x15 = internal speaker, + * 0x1b = port replicator headphone out */ #define ALC_HP_EVENT 0x37 @@ -8593,6 +8630,14 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec, static struct hda_verb alc262_fujitsu_unsol_verbs[] = { {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {} }; @@ -8633,12 +8678,16 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; + unsigned int present_int_hp, present_dock_hp; /* need to execute and sync at first */ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + present_int_hp = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0); + present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->jack_present |= (present_dock_hp & 0x80000000) != 0; spec->sense_updated = 1; } if (spec->jack_present) { @@ -8672,6 +8721,46 @@ static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { }, }; +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || !spec->sense_updated) { + unsigned int present_int_hp; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); + present_int_hp = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present_int_hp & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_lenovo_3000_automute(codec, 1); +} + /* bind hp and internal speaker mute (with plug check) */ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -8680,12 +8769,13 @@ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change; - change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); + change = snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + change |= snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + if (change) alc262_fujitsu_automute(codec, 0); return change; @@ -8703,6 +8793,46 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { }, HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ +}; + +/* bind hp and internal speaker mute (with plug check) */ +static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + valp ? 0 : HDA_AMP_MUTE); + + if (change) + alc262_lenovo_3000_automute(codec, 0); + return change; +} + +static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc262_lenovo_3000_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), @@ -8730,59 +8860,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { /* Samsung Q1 Ultra Vista model setup */ static struct snd_kcontrol_new alc262_ultra_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT), { } /* end */ }; static struct hda_verb alc262_ultra_verbs[] = { - {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* output mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* speaker */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, - /* Mic is on Node 0x19 */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, - {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + /* internal mic */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* ADC, choose mic */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)}, {} }; -static struct hda_input_mux alc262_ultra_capture_source = { - .num_items = 1, - .items = { - { "Mic", 0x1 }, - }, -}; - /* mute/unmute internal speaker according to the hp jack and mute state */ static void alc262_ultra_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int mute; - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - if (spec->jack_present) { - /* mute internal speaker */ - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); - } else { - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); + mute = 0; + /* auto-mute only when HP is used as HP */ + if (!spec->cur_mux[0]) { + unsigned int present; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + if (spec->jack_present) + mute = HDA_AMP_MUTE; } + /* mute/unmute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + /* mute/unmute HP */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE); } /* unsolicited event for HP jack sensing */ @@ -8794,6 +8937,45 @@ static void alc262_ultra_unsol_event(struct hda_codec *codec, alc262_ultra_automute(codec); } +static struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x1 }, + { "Headphone", 0x7 }, + }, +}; + +static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int ret; + + ret = alc882_mux_enum_put(kcontrol, ucontrol); + if (!ret) + return 0; + /* reprogram the HP pin as mic or HP according to the input source */ + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->cur_mux[0] ? PIN_VREF80 : PIN_HP); + alc262_ultra_automute(codec); /* mute/unmute HP */ + return ret; +} + +static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = alc882_mux_enum_info, + .get = alc882_mux_enum_get, + .put = alc262_ultra_mux_enum_put, + }, + { } /* end */ +}; + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) @@ -9185,9 +9367,12 @@ static int alc262_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc262_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc262_auto_init_multi_out(codec); alc262_auto_init_hp_out(codec); alc262_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -9206,6 +9391,7 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_BENQ_T31] = "benq-t31", [ALC262_SONY_ASSAMD] = "sony-assamd", [ALC262_ULTRA] = "ultra", + [ALC262_LENOVO_3000] = "lenovo-3000", [ALC262_AUTO] = "auto", }; @@ -9241,6 +9427,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc039, "Samsung Q1U EL", ALC262_ULTRA), + SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), @@ -9390,18 +9578,32 @@ static struct alc_config_preset alc262_presets[] = { .init_hook = alc262_hippo_automute, }, [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer }, - .init_verbs = { alc262_init_verbs, alc262_ultra_verbs }, + .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, + .init_verbs = { alc262_ultra_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_ultra_capture_source, + .adc_nids = alc262_adc_nids, /* ADC0 */ + .capsrc_nids = alc262_capsrc_nids, + .num_adc_nids = 1, /* single ADC */ .unsol_event = alc262_ultra_unsol_event, .init_hook = alc262_ultra_automute, }, + [ALC262_LENOVO_3000] = { + .mixers = { alc262_lenovo_3000_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, + alc262_lenovo_3000_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc262_lenovo_3000_unsol_event, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -9472,12 +9674,14 @@ static int patch_alc262(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc262_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); + spec->capsrc_nids = alc262_capsrc_nids_alt; spec->mixers[spec->num_mixers] = alc262_capture_alt_mixer; spec->num_mixers++; } else { spec->adc_nids = alc262_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); + spec->capsrc_nids = alc262_capsrc_nids; spec->mixers[spec->num_mixers] = alc262_capture_mixer; spec->num_mixers++; } @@ -9517,6 +9721,8 @@ static hda_nid_t alc268_adc_nids_alt[1] = { 0x08 }; +static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 }; + static struct snd_kcontrol_new alc268_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), @@ -9529,6 +9735,22 @@ static struct snd_kcontrol_new alc268_base_mixer[] = { { } }; +/* bind Beep switches of both NID 0x0f and 0x10 */ +static struct hda_bind_ctls alc268_bind_beep_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT), + HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT), + 0 + }, +}; + +static struct snd_kcontrol_new alc268_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), + HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw), + { } +}; + static struct hda_verb alc268_eapd_verbs[] = { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, @@ -9613,8 +9835,12 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { }; static struct hda_verb alc268_acer_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, { } @@ -9685,6 +9911,64 @@ static void alc268_dell_unsol_event(struct hda_codec *codec, #define alc268_dell_init_hook alc268_dell_automute +static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + { } +}; + +static struct hda_verb alc267_quanta_il1_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, + { } +}; + +static void alc267_quanta_il1_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + present ? 0 : PIN_OUT); +} + +static void alc267_quanta_il1_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, + present ? 0x00 : 0x01); +} + +static void alc267_quanta_il1_automute(struct hda_codec *codec) +{ + alc267_quanta_il1_hp_automute(codec); + alc267_quanta_il1_mic_automute(codec); +} + +static void alc267_quanta_il1_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc267_quanta_il1_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + alc267_quanta_il1_mic_automute(codec); + break; + } +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -9725,7 +10009,11 @@ static struct hda_verb alc268_base_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Unmute Selector 23h,24h and set the default input to mic-in */ @@ -9764,29 +10052,17 @@ static struct hda_verb alc268_volume_init_verbs[] = { {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* set PCBEEP vol = 0 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))}, + /* set PCBEEP vol = 0, mute connections */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, { } }; #define alc268_mux_enum_info alc_mux_enum_info #define alc268_mux_enum_get alc_mux_enum_get - -static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; - hda_nid_t nid = capture_mixers[adc_idx]; - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - nid, - &spec->cur_mux[adc_idx]); -} +#define alc268_mux_enum_put alc_mux_enum_put static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), @@ -9836,13 +10112,17 @@ static struct hda_input_mux alc268_capture_source = { }, }; +static struct hda_input_mux alc268_acer_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x6 }, + { "Line", 0x2 }, + }, +}; + #ifdef CONFIG_SND_DEBUG static struct snd_kcontrol_new alc268_test_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - /* Volume widgets */ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), @@ -9981,6 +10261,10 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, case 0x1c: idx1 = 3; /* CD */ break; + case 0x12: + case 0x13: + idx1 = 6; /* digital mics */ + break; default: continue; } @@ -10073,6 +10357,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (spec->kctl_alloc) spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->autocfg.speaker_pins[0] != 0x1d) + spec->mixers[spec->num_mixers++] = alc268_beep_mixer; + spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -10091,20 +10378,25 @@ static int alc268_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc268_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc268_auto_init_multi_out(codec); alc268_auto_init_hp_out(codec); alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* * configuration and preset */ static const char *alc268_models[ALC268_MODEL_LAST] = { + [ALC267_QUANTA_IL1] = "quanta-il1", [ALC268_3ST] = "3stack", [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", [ALC268_DELL] = "dell", + [ALC268_ZEPTO] = "zepto", #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = "test", #endif @@ -10112,6 +10404,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { }; static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), @@ -10122,17 +10415,36 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), + SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), + SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO), {} }; static struct alc_config_preset alc268_presets[] = { + [ALC267_QUANTA_IL1] = { + .mixers = { alc267_quanta_il1_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc267_quanta_il1_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc267_quanta_il1_unsol_event, + .init_hook = alc267_quanta_il1_automute, + }, [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10140,13 +10452,15 @@ static struct alc_config_preset alc268_presets[] = { .input_mux = &alc268_capture_source, }, [ALC268_TOSHIBA] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, @@ -10155,22 +10469,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_toshiba_automute, }, [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer }, + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x02, .num_channel_mode = ARRAY_SIZE(alc268_modes), .channel_mode = alc268_modes, - .input_mux = &alc268_capture_source, + .input_mux = &alc268_acer_capture_source, .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, [ALC268_DELL] = { - .mixers = { alc268_dell_mixer }, + .mixers = { alc268_dell_mixer, alc268_beep_mixer }, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_dell_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -10182,6 +10498,24 @@ static struct alc_config_preset alc268_presets[] = { .init_hook = alc268_dell_init_hook, .input_mux = &alc268_capture_source, }, + [ALC268_ZEPTO] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc268_toshiba_unsol_event, + .init_hook = alc268_toshiba_automute + }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { .mixers = { alc268_test_mixer, alc268_capture_mixer }, @@ -10191,6 +10525,7 @@ static struct alc_config_preset alc268_presets[] = { .dac_nids = alc268_dac_nids, .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, .hp_nid = 0x03, .dig_out_nid = ALC268_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc268_modes), @@ -10247,13 +10582,22 @@ static int patch_alc268(struct hda_codec *codec) spec->stream_name_digital = "ALC268 Digital"; spec->stream_digital_playback = &alc268_pcm_digital_playback; + if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) + /* override the amp caps for beep generator */ + snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, + (0x0c << AC_AMPCAP_OFFSET_SHIFT) | + (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); + int i; /* get type */ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); spec->mixers[spec->num_mixers] = @@ -10266,6 +10610,12 @@ static int patch_alc268(struct hda_codec *codec) alc268_capture_mixer; spec->num_mixers++; } + spec->capsrc_nids = alc268_capsrc_nids; + /* set default input source */ + for (i = 0; i < spec->num_adc_nids; i++) + snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], + 0, AC_VERB_SET_CONNECT_SEL, + spec->input_mux->items[0].index); } spec->vmaster_nid = 0x02; @@ -10539,9 +10889,12 @@ static int alc269_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static void alc269_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc269_auto_init_multi_out(codec); alc269_auto_init_hp_out(codec); alc269_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } /* @@ -11463,13 +11816,7 @@ static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); - snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - + alc_set_pin_output(codec, nid, pin_type); } static void alc861_auto_init_multi_out(struct hda_codec *codec) @@ -11496,6 +11843,9 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ alc861_auto_set_output_and_unmute(codec, pin, PIN_HP, spec->multiout.dac_nids[0]); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } static void alc861_auto_init_analog_input(struct hda_codec *codec) @@ -11568,9 +11918,12 @@ static int alc861_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861_auto_init_multi_out(codec); alc861_auto_init_hp_out(codec); alc861_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -11822,6 +12175,8 @@ static hda_nid_t alc861vd_adc_nids[1] = { 0x09, }; +static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ static struct hda_input_mux alc861vd_capture_source = { @@ -11835,11 +12190,10 @@ static struct hda_input_mux alc861vd_capture_source = { }; static struct hda_input_mux alc861vd_dallas_capture_source = { - .num_items = 3, + .num_items = 2, .items = { - { "Front Mic", 0x0 }, - { "ATAPI Mic", 0x1 }, - { "Line In", 0x5 }, + { "Ext Mic", 0x0 }, + { "Int Mic", 0x1 }, }, }; @@ -11853,33 +12207,8 @@ static struct hda_input_mux alc861vd_hp_capture_source = { #define alc861vd_mux_enum_info alc_mux_enum_info #define alc861vd_mux_enum_get alc_mux_enum_get - -static int alc861vd_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[1] = { 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} +/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ +#define alc861vd_mux_enum_put alc882_mux_enum_put /* * 2ch mode @@ -12034,20 +12363,22 @@ static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = { { } /* end */ }; -/* Pin assignment: Front=0x14, HP = 0x15, - * Front Mic=0x18, ATAPI Mic = 0x19, Line In = 0x1d +/* Pin assignment: Speaker=0x14, HP = 0x15, + * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d */ static struct snd_kcontrol_new alc861vd_dallas_mixer[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Switch", 0x0b, 0x05, HDA_INPUT), { } /* end */ }; @@ -12348,6 +12679,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS), SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), @@ -12362,8 +12694,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_3stack_init_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12375,8 +12705,6 @@ static struct alc_config_preset alc861vd_presets[] = { .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, .dig_out_nid = ALC861VD_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12421,8 +12749,6 @@ static struct alc_config_preset alc861vd_presets[] = { alc861vd_lenovo_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), .dac_nids = alc660vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, @@ -12434,8 +12760,6 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_dallas_capture_source, @@ -12447,9 +12771,7 @@ static struct alc_config_preset alc861vd_presets[] = { .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), .dac_nids = alc861vd_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), .dig_out_nid = ALC861VD_DIGOUT_NID, - .adc_nids = alc861vd_adc_nids, .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_hp_capture_source, @@ -12464,11 +12786,7 @@ static struct alc_config_preset alc861vd_presets[] = { static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); } static void alc861vd_auto_init_multi_out(struct hda_codec *codec) @@ -12495,6 +12813,9 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec) pin = spec->autocfg.hp_pins[0]; if (pin) /* connect to front and use dac 0 */ alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc861vd_is_input_pin(nid) alc880_is_input_pin(nid) @@ -12698,9 +13019,12 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc861vd_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc861vd_auto_init_multi_out(codec); alc861vd_auto_init_hp_out(codec); alc861vd_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc861vd(struct hda_codec *codec) @@ -12751,6 +13075,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->adc_nids = alc861vd_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); + spec->capsrc_nids = alc861vd_capsrc_nids; spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; spec->num_mixers++; @@ -12792,9 +13117,11 @@ static hda_nid_t alc662_adc_nids[1] = { /* ADC1-2 */ 0x09, }; + +static hda_nid_t alc662_capsrc_nids[1] = { 0x22 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ - static struct hda_input_mux alc662_capture_source = { .num_items = 4, .items = { @@ -12823,33 +13150,8 @@ static struct hda_input_mux alc662_eeepc_capture_source = { #define alc662_mux_enum_info alc_mux_enum_info #define alc662_mux_enum_get alc_mux_enum_get +#define alc662_mux_enum_put alc882_mux_enum_put -static int alc662_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[2] = { 0x23, 0x22 }; - hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} /* * 2ch mode */ @@ -12918,13 +13220,13 @@ static struct hda_channel_mode alc662_5stack_modes[2] = { static struct snd_kcontrol_new alc662_base_mixer[] = { /* output mixer control */ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), /*Input mixer control */ @@ -12941,7 +13243,7 @@ static struct snd_kcontrol_new alc662_base_mixer[] = { static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), @@ -12958,13 +13260,13 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), @@ -13313,6 +13615,7 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { }; static struct snd_pci_quirk alc662_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), @@ -13326,8 +13629,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, @@ -13340,8 +13641,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, @@ -13354,8 +13653,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .need_dac_fix = 1, @@ -13368,8 +13665,6 @@ static struct alc_config_preset alc662_presets[] = { .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, .dig_out_nid = ALC662_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .dig_in_nid = ALC662_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), .channel_mode = alc662_5stack_modes, @@ -13380,8 +13675,6 @@ static struct alc_config_preset alc662_presets[] = { .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13394,8 +13687,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), .channel_mode = alc662_3ST_2ch_modes, .input_mux = &alc662_eeepc_capture_source, @@ -13409,8 +13700,6 @@ static struct alc_config_preset alc662_presets[] = { alc662_eeepc_ep20_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), - .adc_nids = alc662_adc_nids, .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), .channel_mode = alc662_3ST_6ch_modes, .input_mux = &alc662_lenovo_101e_capture_source, @@ -13556,11 +13845,7 @@ static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) { - /* set as output */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + alc_set_pin_output(codec, nid, pin_type); /* need the manual connection? */ if (alc880_is_multi_pin(nid)) { struct alc_spec *spec = codec->spec; @@ -13595,6 +13880,9 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) if (pin) /* connect to front */ /* use dac 0 */ alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + pin = spec->autocfg.speaker_pins[0]; + if (pin) + alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } #define alc662_is_input_pin(nid) alc880_is_input_pin(nid) @@ -13672,9 +13960,12 @@ static int alc662_parse_auto_config(struct hda_codec *codec) /* additional initialization for auto-configuration model */ static void alc662_auto_init(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; alc662_auto_init_multi_out(codec); alc662_auto_init_hp_out(codec); alc662_auto_init_analog_input(codec); + if (spec->unsol_event) + alc_sku_automute(codec); } static int patch_alc662(struct hda_codec *codec) @@ -13722,10 +14013,9 @@ static int patch_alc662(struct hda_codec *codec) spec->stream_digital_playback = &alc662_pcm_digital_playback; spec->stream_digital_capture = &alc662_pcm_digital_capture; - if (!spec->adc_nids && spec->input_mux) { - spec->adc_nids = alc662_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); - } + spec->adc_nids = alc662_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); + spec->capsrc_nids = alc662_capsrc_nids; spec->vmaster_nid = 0x02; @@ -13761,6 +14051,8 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, + { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", + .patch = patch_alc882 }, /* should be patch_alc883() in future */ { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index d22f5a6b850..9332b63e406 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -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; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index caf48edaa92..b3a15d61687 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -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; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4e5dd4cf36f..52b1d81a26f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -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); diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index efd180b40e5..0ed96c17805 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -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; diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index 26ea05a32f5..ea7116c304c 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -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 diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 064760d2a02..013fc4f0482 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -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; @@ -1049,6 +1057,13 @@ struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = { .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", .model = "ews88d", diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h index e4ed1b475b0..1c443718af0 100644 --- a/sound/pci/ice1712/ews.h +++ b/sound/pci/ice1712/ews.h @@ -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[]; diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index cf5c7c0898f..6914189073a 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -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 */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index df292af6738..29d449d73c9 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -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); diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 303cffe08bd..3208901c740 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -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, diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index f533850ec6e..4490422fb93 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -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; } /* @@ -219,6 +223,32 @@ static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice) } /* + * 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; - +#ifdef CONFIG_SND_DEBUG + if (++timeout > 10) { + printk(KERN_ERR + "ice1724: Too long irq loop, status = 0x%x\n", + status); + break; + } +#endif 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 (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); - outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT)); - status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); + 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 } } diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index e8038c0ceb7..b4e0c16852a 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -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; @@ -44,6 +46,32 @@ struct juli_spec { #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 */ #define GPIO_FREQ_MASK (3<<0) @@ -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) { @@ -78,6 +171,27 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char 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, }; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 4945c81e8a9..203cdc1bf8d 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -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; } diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 48cf40a8f32..48d3679292a 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -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 */ }; diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 301bf929acd..4d2631434dc 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -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; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index c52abd0bf22..048d99e25ab 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -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; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index cadda8d6b70..faf674e671a 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -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; } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 10c713d9ac4..f4c85b52bde 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2102,7 +2102,6 @@ snd_korg1212_free(struct snd_korg1212 *korg1212) snd_korg1212_TurnOffIdleMonitor(korg1212); if (korg1212->irq >= 0) { - synchronize_irq(korg1212->irq); snd_korg1212_DisableCardInterrupts(korg1212); free_irq(korg1212->irq, korg1212); korg1212->irq = -1; diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 04fa0a68416..a536c59fbea 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2068,7 +2068,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *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_m3_ac97_write, @@ -2088,14 +2088,14 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) schedule_timeout_uninterruptible(msecs_to_jiffies(100)); snd_ac97_write(chip->ac97, AC97_PCM, 0); - 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; } @@ -2542,10 +2542,8 @@ static int snd_m3_free(struct snd_m3 *chip) vfree(chip->suspend_mem); #endif - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } if (chip->iobase) pci_release_regions(chip->pci); @@ -2569,7 +2567,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2583,12 +2581,12 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) snd_m3_assp_halt(chip); /* save dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_CODE, i); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) - chip->suspend_mem[index++] = + chip->suspend_mem[dsp_index++] = snd_m3_assp_read(chip, MEMTYPE_INTERNAL_DATA, i); pci_disable_device(pci); @@ -2601,7 +2599,7 @@ static int m3_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct snd_m3 *chip = card->private_data; - int i, index; + int i, dsp_index; if (chip->suspend_mem == NULL) return 0; @@ -2625,13 +2623,13 @@ static int m3_resume(struct pci_dev *pci) snd_m3_ac97_reset(chip); /* restore dsp image */ - index = 0; + dsp_index = 0; for (i = REV_B_CODE_MEMORY_BEGIN; i <= REV_B_CODE_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); for (i = REV_B_DATA_MEMORY_BEGIN ; i <= REV_B_DATA_MEMORY_END; i++) snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, i, - chip->suspend_mem[index++]); + chip->suspend_mem[dsp_index++]); /* tell the dma engine to restart itself */ snd_m3_assp_write(chip, MEMTYPE_INTERNAL_DATA, diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 7ac654e381d..7efb838d18a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1439,7 +1439,7 @@ static int snd_nm256_free(struct nm256 *chip) snd_nm256_capture_stop(chip); if (chip->irq >= 0) - synchronize_irq(chip->irq); + free_irq(chip->irq, chip); if (chip->cport) iounmap(chip->cport); @@ -1447,8 +1447,6 @@ static int snd_nm256_free(struct nm256 *chip) iounmap(chip->buffer); release_and_free_resource(chip->res_cport); release_and_free_resource(chip->res_buffer); - if (chip->irq >= 0) - free_irq(chip->irq, chip); pci_disable_device(chip->pci); kfree(chip->ac97_regs); diff --git a/sound/pci/oxygen/cs4362a.h b/sound/pci/oxygen/cs4362a.h new file mode 100644 index 00000000000..6a4fedf5e1e --- /dev/null +++ b/sound/pci/oxygen/cs4362a.h @@ -0,0 +1,69 @@ +/* register 01h */ +#define CS4362A_PDN 0x01 +#define CS4362A_DAC1_DIS 0x02 +#define CS4362A_DAC2_DIS 0x04 +#define CS4362A_DAC3_DIS 0x08 +#define CS4362A_MCLKDIV 0x20 +#define CS4362A_FREEZE 0x40 +#define CS4362A_CPEN 0x80 +/* register 02h */ +#define CS4362A_DIF_MASK 0x70 +#define CS4362A_DIF_LJUST 0x00 +#define CS4362A_DIF_I2S 0x10 +#define CS4362A_DIF_RJUST_16 0x20 +#define CS4362A_DIF_RJUST_24 0x30 +#define CS4362A_DIF_RJUST_20 0x40 +#define CS4362A_DIF_RJUST_18 0x50 +/* register 03h */ +#define CS4362A_MUTEC_MASK 0x03 +#define CS4362A_MUTEC_6 0x00 +#define CS4362A_MUTEC_1 0x01 +#define CS4362A_MUTEC_3 0x03 +#define CS4362A_AMUTE 0x04 +#define CS4362A_MUTEC_POL 0x08 +#define CS4362A_RMP_UP 0x10 +#define CS4362A_SNGLVOL 0x20 +#define CS4362A_ZERO_CROSS 0x40 +#define CS4362A_SOFT_RAMP 0x80 +/* register 04h */ +#define CS4362A_RMP_DN 0x01 +#define CS4362A_DEM_MASK 0x06 +#define CS4362A_DEM_NONE 0x00 +#define CS4362A_DEM_44100 0x02 +#define CS4362A_DEM_48000 0x04 +#define CS4362A_DEM_32000 0x06 +#define CS4362A_FILT_SEL 0x10 +/* register 05h */ +#define CS4362A_INV_A1 0x01 +#define CS4362A_INV_B1 0x02 +#define CS4362A_INV_A2 0x04 +#define CS4362A_INV_B2 0x08 +#define CS4362A_INV_A3 0x10 +#define CS4362A_INV_B3 0x20 +/* register 06h */ +#define CS4362A_FM_MASK 0x03 +#define CS4362A_FM_SINGLE 0x00 +#define CS4362A_FM_DOUBLE 0x01 +#define CS4362A_FM_QUAD 0x02 +#define CS4362A_FM_DSD 0x03 +#define CS4362A_ATAPI_MASK 0x7c +#define CS4362A_ATAPI_B_MUTE 0x00 +#define CS4362A_ATAPI_B_R 0x04 +#define CS4362A_ATAPI_B_L 0x08 +#define CS4362A_ATAPI_B_LR 0x0c +#define CS4362A_ATAPI_A_MUTE 0x00 +#define CS4362A_ATAPI_A_R 0x10 +#define CS4362A_ATAPI_A_L 0x20 +#define CS4362A_ATAPI_A_LR 0x30 +#define CS4362A_ATAPI_MIX_LR_VOL 0x40 +#define CS4362A_A_EQ_B 0x80 +/* register 07h */ +#define CS4362A_VOL_MASK 0x7f +#define CS4362A_MUTE 0x80 +/* register 08h: like 07h */ +/* registers 09h..0Bh: like 06h..08h */ +/* registers 0Ch..0Eh: like 06h..08h */ +/* register 12h */ +#define CS4362A_REV_MASK 0x07 +#define CS4362A_PART_MASK 0xf8 +#define CS4362A_PART_CS4362A 0x50 diff --git a/sound/pci/oxygen/cs4398.h b/sound/pci/oxygen/cs4398.h new file mode 100644 index 00000000000..5faf5efc882 --- /dev/null +++ b/sound/pci/oxygen/cs4398.h @@ -0,0 +1,69 @@ +/* register 1 */ +#define CS4398_REV_MASK 0x07 +#define CS4398_PART_MASK 0xf8 +#define CS4398_PART_CS4398 0x70 +/* register 2 */ +#define CS4398_FM_MASK 0x03 +#define CS4398_FM_SINGLE 0x00 +#define CS4398_FM_DOUBLE 0x01 +#define CS4398_FM_QUAD 0x02 +#define CS4398_FM_DSD 0x03 +#define CS4398_DEM_MASK 0x0c +#define CS4398_DEM_NONE 0x00 +#define CS4398_DEM_44100 0x04 +#define CS4398_DEM_48000 0x08 +#define CS4398_DEM_32000 0x0c +#define CS4398_DIF_MASK 0x70 +#define CS4398_DIF_LJUST 0x00 +#define CS4398_DIF_I2S 0x10 +#define CS4398_DIF_RJUST_16 0x20 +#define CS4398_DIF_RJUST_24 0x30 +#define CS4398_DIF_RJUST_20 0x40 +#define CS4398_DIF_RJUST_18 0x50 +#define CS4398_DSD_SRC 0x80 +/* register 3 */ +#define CS4398_ATAPI_MASK 0x1f +#define CS4398_ATAPI_B_MUTE 0x00 +#define CS4398_ATAPI_B_R 0x01 +#define CS4398_ATAPI_B_L 0x02 +#define CS4398_ATAPI_B_LR 0x03 +#define CS4398_ATAPI_A_MUTE 0x00 +#define CS4398_ATAPI_A_R 0x04 +#define CS4398_ATAPI_A_L 0x08 +#define CS4398_ATAPI_A_LR 0x0c +#define CS4398_ATAPI_MIX_LR_VOL 0x10 +#define CS4398_INVERT_B 0x20 +#define CS4398_INVERT_A 0x40 +#define CS4398_VOL_B_EQ_A 0x80 +/* register 4 */ +#define CS4398_MUTEP_MASK 0x03 +#define CS4398_MUTEP_AUTO 0x00 +#define CS4398_MUTEP_LOW 0x02 +#define CS4398_MUTEP_HIGH 0x03 +#define CS4398_MUTE_B 0x08 +#define CS4398_MUTE_A 0x10 +#define CS4398_MUTEC_A_EQ_B 0x20 +#define CS4398_DAMUTE 0x40 +#define CS4398_PAMUTE 0x80 +/* register 5 */ +#define CS4398_VOL_A_MASK 0xff +/* register 6 */ +#define CS4398_VOL_B_MASK 0xff +/* register 7 */ +#define CS4398_DIR_DSD 0x01 +#define CS4398_FILT_SEL 0x04 +#define CS4398_RMP_DN 0x10 +#define CS4398_RMP_UP 0x20 +#define CS4398_ZERO_CROSS 0x40 +#define CS4398_SOFT_RAMP 0x80 +/* register 8 */ +#define CS4398_MCLKDIV3 0x08 +#define CS4398_MCLKDIV2 0x10 +#define CS4398_FREEZE 0x20 +#define CS4398_CPEN 0x40 +#define CS4398_PDN 0x80 +/* register 9 */ +#define CS4398_DSD_PM_EN 0x01 +#define CS4398_DSD_PM_MODE 0x02 +#define CS4398_INVALID_DSD 0x04 +#define CS4398_STATIC_DSD 0x08 diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 666f69a3312..090dd4354a2 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -66,12 +66,12 @@ static void hifier_init(struct oxygen *chip) { struct hifier_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2); ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); - ak4396_write(chip, AK4396_LCH_ATT, 0xff); - ak4396_write(chip, AK4396_RCH_ATT, 0xff); + ak4396_write(chip, AK4396_LCH_ATT, 0); + ak4396_write(chip, AK4396_RCH_ATT, 0); snd_component_add(chip->card, "AK4396"); snd_component_add(chip->card, "CS5340"); @@ -127,22 +127,8 @@ static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static int hifier_control_filter(struct snd_kcontrol_new *template) { - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->tlv.p = ak4396_db_scale; - } else if (!strcmp(template->name, "Stereo Upmixing")) { + if (!strcmp(template->name, "Stereo Upmixing")) return 1; /* stereo only - we don't need upmixing */ - } else if (!strcmp(template->name, - SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) || - !strcmp(template->name, - SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) { - return 1; /* no digital input */ - } - return 0; -} - -static int hifier_mixer_init(struct oxygen *chip) -{ return 0; } @@ -153,18 +139,20 @@ static const struct oxygen_model model_hifier = { .owner = THIS_MODULE, .init = hifier_init, .control_filter = hifier_control_filter, - .mixer_init = hifier_mixer_init, .cleanup = hifier_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_cs5340_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct hifier_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_1, .dac_channels = 2, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = 0, + .dac_volume_min = 0, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_SPI, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -181,7 +169,7 @@ static int __devinit hifier_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier); + err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 9a9941bb046..63f185c1ed1 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -39,7 +39,7 @@ #include <sound/tlv.h> #include "oxygen.h" #include "ak4396.h" -#include "cm9780.h" +#include "wm8785.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_DESCRIPTION("C-Media CMI8788 driver"); @@ -78,49 +78,6 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #define GPIO_AK5385_DFS_DOUBLE 0x0001 #define GPIO_AK5385_DFS_QUAD 0x0002 -#define GPIO_LINE_MUTE CM9780_GPO0 - -#define WM8785_R0 0 -#define WM8785_R1 1 -#define WM8785_R2 2 -#define WM8785_R7 7 - -/* R0 */ -#define WM8785_MCR_MASK 0x007 -#define WM8785_MCR_SLAVE 0x000 -#define WM8785_MCR_MASTER_128 0x001 -#define WM8785_MCR_MASTER_192 0x002 -#define WM8785_MCR_MASTER_256 0x003 -#define WM8785_MCR_MASTER_384 0x004 -#define WM8785_MCR_MASTER_512 0x005 -#define WM8785_MCR_MASTER_768 0x006 -#define WM8785_OSR_MASK 0x018 -#define WM8785_OSR_SINGLE 0x000 -#define WM8785_OSR_DOUBLE 0x008 -#define WM8785_OSR_QUAD 0x010 -#define WM8785_FORMAT_MASK 0x060 -#define WM8785_FORMAT_RJUST 0x000 -#define WM8785_FORMAT_LJUST 0x020 -#define WM8785_FORMAT_I2S 0x040 -#define WM8785_FORMAT_DSP 0x060 -/* R1 */ -#define WM8785_WL_MASK 0x003 -#define WM8785_WL_16 0x000 -#define WM8785_WL_20 0x001 -#define WM8785_WL_24 0x002 -#define WM8785_WL_32 0x003 -#define WM8785_LRP 0x004 -#define WM8785_BCLKINV 0x008 -#define WM8785_LRSWAP 0x010 -#define WM8785_DEVNO_MASK 0x0e0 -/* R2 */ -#define WM8785_HPFR 0x001 -#define WM8785_HPFL 0x002 -#define WM8785_SDODIS 0x004 -#define WM8785_PWRDNR 0x008 -#define WM8785_PWRDNL 0x010 -#define WM8785_TDM_MASK 0x1c0 - struct generic_data { u8 ak4396_ctl2; }; @@ -155,7 +112,7 @@ static void ak4396_init(struct oxygen *chip) struct generic_data *data = chip->model_data; unsigned int i; - data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; for (i = 0; i < 4; ++i) { ak4396_write(chip, i, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); @@ -163,8 +120,8 @@ static void ak4396_init(struct oxygen *chip) AK4396_CONTROL_2, data->ak4396_ctl2); ak4396_write(chip, i, AK4396_CONTROL_3, AK4396_PCM); - ak4396_write(chip, i, AK4396_LCH_ATT, 0xff); - ak4396_write(chip, i, AK4396_RCH_ATT, 0xff); + ak4396_write(chip, i, AK4396_LCH_ATT, 0); + ak4396_write(chip, i, AK4396_RCH_ATT, 0); } snd_component_add(chip->card, "AK4396"); } @@ -185,23 +142,16 @@ static void wm8785_init(struct oxygen *chip) snd_component_add(chip->card, "WM8785"); } -static void cmi9780_init(struct oxygen *chip) -{ - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); -} - static void generic_init(struct oxygen *chip) { ak4396_init(chip); wm8785_init(chip); - cmi9780_init(chip); } static void meridian_init(struct oxygen *chip) { ak4396_init(chip); ak5385_init(chip); - cmi9780_init(chip); } static void generic_cleanup(struct oxygen *chip) @@ -297,59 +247,32 @@ static void set_ak5385_params(struct oxygen *chip, value, GPIO_AK5385_DFS_MASK); } -static void cmi9780_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - break; - case AC97_MIC: - case AC97_CD: - case AC97_AUX: - if (!mute) - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - break; - } -} - static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); -static int ak4396_control_filter(struct snd_kcontrol_new *template) -{ - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->tlv.p = ak4396_db_scale; - } - return 0; -} - static const struct oxygen_model model_generic = { .shortname = "C-Media CMI8788", .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .owner = THIS_MODULE, .init = generic_init, - .control_filter = ak4396_control_filter, .cleanup = generic_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_wm8785_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_1 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_volume_min = 0, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -359,21 +282,25 @@ static const struct oxygen_model model_meridian = { .chip = "CMI8788", .owner = THIS_MODULE, .init = meridian_init, - .control_filter = ak4396_control_filter, .cleanup = generic_cleanup, .set_dac_params = set_ak4396_params, .set_adc_params = set_ak5385_params, .update_dac_volume = update_ak4396_volume, .update_dac_mute = update_ak4396_mute, - .ac97_switch_hook = cmi9780_switch_hook, + .dac_tlv = ak4396_db_scale, .model_data_size = sizeof(struct generic_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + PLAYBACK_2_TO_AC97_1 | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF | + CAPTURE_2_FROM_AC97_1, .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH | - OXYGEN_CHANNEL_AC97, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_volume_min = 0, + .dac_volume_max = 255, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, }; @@ -392,7 +319,7 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci, return -ENOENT; } is_meridian = pci_id->driver_data; - err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian, + err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian ? &model_meridian : &model_generic); if (err >= 0) ++dev; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index ad50fb8b206..a71c6e05926 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -16,6 +16,16 @@ #define PCM_AC97 5 #define PCM_COUNT 6 +/* model-specific configuration of outputs/inputs */ +#define PLAYBACK_0_TO_I2S 0x001 +#define PLAYBACK_1_TO_SPDIF 0x004 +#define PLAYBACK_2_TO_AC97_1 0x008 +#define CAPTURE_0_FROM_I2S_1 0x010 +#define CAPTURE_0_FROM_I2S_2 0x020 +#define CAPTURE_1_FROM_SPDIF 0x080 +#define CAPTURE_2_FROM_I2S_2 0x100 +#define CAPTURE_2_FROM_AC97_1 0x200 + enum { CONTROL_SPDIF_PCM, CONTROL_SPDIF_INPUT_BITS, @@ -87,12 +97,16 @@ struct oxygen_model { struct snd_pcm_hw_params *params); void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); - void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute); void (*gpio_changed)(struct oxygen *chip); + void (*ac97_switch)(struct oxygen *chip, + unsigned int reg, unsigned int mute); + const unsigned int *dac_tlv; size_t model_data_size; + unsigned int pcm_dev_cfg; u8 dac_channels; - u8 used_channels; + u8 dac_volume_min; + u8 dac_volume_max; + u8 misc_flags; u8 function_flags; u16 dac_i2s_format; u16 adc_i2s_format; @@ -100,7 +114,7 @@ struct oxygen_model { /* oxygen_lib.c */ -int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi, +int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, const struct oxygen_model *model); void oxygen_pci_remove(struct pci_dev *pci); @@ -137,6 +151,7 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, unsigned int index, u16 data, u16 mask); void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); +void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data); static inline void oxygen_set_bits8(struct oxygen *chip, unsigned int reg, u8 value) diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 74e23ef9c94..5569606ee87 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -190,12 +190,31 @@ void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) --count; } - spin_lock_irq(&chip->reg_lock); oxygen_write8(chip, OXYGEN_SPI_DATA1, data); oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); if (control & OXYGEN_SPI_DATA_LENGTH_3) oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); - spin_unlock_irq(&chip->reg_lock); } EXPORT_SYMBOL(oxygen_write_spi); + +void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data) +{ + unsigned long timeout; + + /* should not need more than about 300 us */ + timeout = jiffies + msecs_to_jiffies(1); + do { + if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS) + & OXYGEN_2WIRE_BUSY)) + break; + udelay(1); + cond_resched(); + } while (time_after_eq(timeout, jiffies)); + + oxygen_write8(chip, OXYGEN_2WIRE_MAP, map); + oxygen_write8(chip, OXYGEN_2WIRE_DATA, data); + oxygen_write8(chip, OXYGEN_2WIRE_CONTROL, + device | OXYGEN_2WIRE_DIR_WRITE); +} +EXPORT_SYMBOL(oxygen_write_i2c); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 78c21155218..897697d4350 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -221,7 +221,8 @@ static void oxygen_init(struct oxygen *chip) chip->dac_routing = 1; for (i = 0; i < 8; ++i) - chip->dac_volume[i] = 0xff; + chip->dac_volume[i] = chip->model->dac_volume_min; + chip->dac_mute = 1; chip->spdif_playback_enable = 1; chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT); @@ -240,12 +241,12 @@ static void oxygen_init(struct oxygen *chip) chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0; chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0; - oxygen_set_bits8(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_RESET_CODEC | - chip->model->function_flags); oxygen_write8_masked(chip, OXYGEN_FUNCTION, - OXYGEN_FUNCTION_SPI, - OXYGEN_FUNCTION_2WIRE_SPI_MASK); + OXYGEN_FUNCTION_RESET_CODEC | + chip->model->function_flags, + OXYGEN_FUNCTION_RESET_CODEC | + OXYGEN_FUNCTION_2WIRE_SPI_MASK | + OXYGEN_FUNCTION_ENABLE_SPI_4_5); oxygen_write8(chip, OXYGEN_DMA_STATUS, 0); oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0); oxygen_write8(chip, OXYGEN_PLAY_CHANNELS, @@ -253,11 +254,13 @@ static void oxygen_init(struct oxygen *chip) OXYGEN_DMA_A_BURST_8 | OXYGEN_DMA_MULTICH_BURST_8); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); - oxygen_write8_masked(chip, OXYGEN_MISC, 0, + oxygen_write8_masked(chip, OXYGEN_MISC, + chip->model->misc_flags, OXYGEN_MISC_WRITE_PCI_SUBID | OXYGEN_MISC_REC_C_FROM_SPDIF | OXYGEN_MISC_REC_B_FROM_AC97 | - OXYGEN_MISC_REC_A_FROM_MULTICH); + OXYGEN_MISC_REC_A_FROM_MULTICH | + OXYGEN_MISC_MIDI); oxygen_write8(chip, OXYGEN_REC_FORMAT, (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) | (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) | @@ -267,35 +270,49 @@ static void oxygen_init(struct oxygen *chip) (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT)); oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_RATE_48000 | chip->model->dac_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); + if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 | + CAPTURE_2_FROM_I2S_2)) + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_RATE_48000 | chip->model->adc_i2s_format | + OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + else + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, - OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | - OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | - OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); - oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, - OXYGEN_SPDIF_SENSE_MASK | - OXYGEN_SPDIF_LOCK_MASK | - OXYGEN_SPDIF_RATE_MASK | - OXYGEN_SPDIF_LOCK_PAR | - OXYGEN_SPDIF_IN_CLOCK_96, - OXYGEN_SPDIF_OUT_ENABLE | - OXYGEN_SPDIF_LOOPBACK | - OXYGEN_SPDIF_SENSE_MASK | - OXYGEN_SPDIF_LOCK_MASK | - OXYGEN_SPDIF_RATE_MASK | - OXYGEN_SPDIF_SENSE_PAR | - OXYGEN_SPDIF_LOCK_PAR | - OXYGEN_SPDIF_IN_CLOCK_MASK); + OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE | + OXYGEN_SPDIF_LOOPBACK); + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_96, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_SENSE_PAR | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_MASK); + else + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK); oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); @@ -318,9 +335,12 @@ static void oxygen_init(struct oxygen *chip) (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) | (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT)); - oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, - OXYGEN_AC97_INT_READ_DONE | - OXYGEN_AC97_INT_WRITE_DONE); + if (chip->has_ac97_0 | chip->has_ac97_1) + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, + OXYGEN_AC97_INT_READ_DONE | + OXYGEN_AC97_INT_WRITE_DONE); + else + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0); oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0); oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0); if (!(chip->has_ac97_0 | chip->has_ac97_1)) @@ -351,6 +371,8 @@ static void oxygen_init(struct oxygen *chip) oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000); oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080); oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080); + oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0); /* power down unused ADCs and DACs */ oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN, AC97_PD_PR0 | AC97_PD_PR1); @@ -388,10 +410,8 @@ static void oxygen_card_free(struct snd_card *card) oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); spin_unlock_irq(&chip->reg_lock); - if (chip->irq >= 0) { + if (chip->irq >= 0) free_irq(chip->irq, chip); - synchronize_irq(chip->irq); - } flush_scheduled_work(); chip->model->cleanup(chip); mutex_destroy(&chip->mutex); @@ -400,7 +420,7 @@ static void oxygen_card_free(struct snd_card *card) } int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, - int midi, const struct oxygen_model *model) + const struct oxygen_model *model) { struct snd_card *card; struct oxygen *chip; @@ -472,9 +492,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, if (err < 0) goto err_card; - oxygen_write8_masked(chip, OXYGEN_MISC, - midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI); - if (midi) { + if (model->misc_flags & OXYGEN_MISC_MIDI) { err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, chip->addr + OXYGEN_MPU401, MPU401_INFO_INTEGRATED, 0, 0, @@ -486,7 +504,10 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, oxygen_proc_init(chip); spin_lock_irq(&chip->reg_lock); - chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97; + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT; + if (chip->has_ac97_0 | chip->has_ac97_1) + chip->interrupt_mask |= OXYGEN_INT_AC97; oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); spin_unlock_irq(&chip->reg_lock); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index a8e4623415d..cc0cddadd58 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -32,8 +32,8 @@ static int dac_volume_info(struct snd_kcontrol *ctl, info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = chip->model->dac_channels; - info->value.integer.min = 0; - info->value.integer.max = 0xff; + info->value.integer.min = chip->model->dac_volume_min; + info->value.integer.max = chip->model->dac_volume_max; return 0; } @@ -446,6 +446,50 @@ static int spdif_loopback_put(struct snd_kcontrol *ctl, return changed; } +static int monitor_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 1; + return 0; +} + +static int monitor_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + + value->value.integer.value[0] = + !!invert ^ !!(oxygen_read8(chip, OXYGEN_ADC_MONITOR) & bit); + return 0; +} + +static int monitor_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u8 bit = ctl->private_value; + int invert = ctl->private_value & (1 << 8); + u8 oldreg, newreg; + int changed; + + spin_lock_irq(&chip->reg_lock); + oldreg = oxygen_read8(chip, OXYGEN_ADC_MONITOR); + if ((!!value->value.integer.value[0] ^ !!invert) != 0) + newreg = oldreg | bit; + else + newreg = oldreg & ~bit; + changed = newreg != oldreg; + if (changed) + oxygen_write8(chip, OXYGEN_ADC_MONITOR, newreg); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + static int ac97_switch_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -466,6 +510,21 @@ static int ac97_switch_get(struct snd_kcontrol *ctl, return 0; } +static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) +{ + unsigned int priv_idx = chip->controls[control]->private_value & 0xff; + u16 value; + + value = oxygen_read_ac97(chip, 0, priv_idx); + if (!(value & 0x8000)) { + oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000); + if (chip->model->ac97_switch) + chip->model->ac97_switch(chip, priv_idx, 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[control]->id); + } +} + static int ac97_switch_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { @@ -487,9 +546,24 @@ static int ac97_switch_put(struct snd_kcontrol *ctl, change = newreg != oldreg; if (change) { oxygen_write_ac97(chip, codec, index, newreg); - if (bitnr == 15 && chip->model->ac97_switch_hook) - chip->model->ac97_switch_hook(chip, codec, index, - newreg & 0x8000); + if (codec == 0 && chip->model->ac97_switch) + chip->model->ac97_switch(chip, index, newreg & 0x8000); + if (index == AC97_LINE) { + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + newreg & 0x8000 ? + CM9780_GPO0 : 0, CM9780_GPO0); + if (!(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); + } + } else if ((index == AC97_MIC || index == AC97_CD || + index == AC97_VIDEO || index == AC97_AUX) && + bitnr == 15 && !(newreg & 0x8000)) { + mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + CM9780_GPO0, CM9780_GPO0); + } } mutex_unlock(&chip->mutex); return change; @@ -608,6 +682,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, .private_value = ((codec) << 24) | (index), \ } +static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0); static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); @@ -667,6 +742,9 @@ static const struct snd_kcontrol_new controls[] = { .get = spdif_pcm_get, .put = spdif_pcm_put, }, +}; + +static const struct snd_kcontrol_new spdif_input_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .device = 1, @@ -692,11 +770,118 @@ static const struct snd_kcontrol_new controls[] = { }, }; +static const struct { + unsigned int pcm_dev; + struct snd_kcontrol_new controls[2]; +} monitor_controls[] = { + { + .pcm_dev = CAPTURE_0_FROM_I2S_1, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_A_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_0_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_2_FROM_I2S_2, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Switch", + .index = 1, + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Monitor Volume", + .index = 1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_B_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, + { + .pcm_dev = CAPTURE_1_FROM_SPDIF, + .controls = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Switch", + .info = snd_ctl_boolean_mono_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Monitor Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = monitor_volume_info, + .get = monitor_get, + .put = monitor_put, + .private_value = OXYGEN_ADC_MONITOR_C_HALF_VOL + | (1 << 8), + .tlv = { .p = monitor_db_scale, }, + }, + }, + }, +}; + static const struct snd_kcontrol_new ac97_controls[] = { AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), - AC97_VOLUME("Line Capture Volume", 0, AC97_LINE), AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), AC97_VOLUME("CD Capture Volume", 0, AC97_CD), AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), @@ -756,6 +941,11 @@ static int add_controls(struct oxygen *chip, return err; if (err == 1) continue; + if (!strcmp(template.name, "Master Playback Volume") && + chip->model->dac_tlv) { + template.tlv.p = chip->model->dac_tlv; + template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + } ctl = snd_ctl_new1(&template, chip); if (!ctl) return -ENOMEM; @@ -773,11 +963,26 @@ static int add_controls(struct oxygen *chip, int oxygen_mixer_init(struct oxygen *chip) { + unsigned int i; int err; err = add_controls(chip, controls, ARRAY_SIZE(controls)); if (err < 0) return err; + if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) { + err = add_controls(chip, spdif_input_controls, + ARRAY_SIZE(spdif_input_controls)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) { + if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev)) + continue; + err = add_controls(chip, monitor_controls[i].controls, + ARRAY_SIZE(monitor_controls[i].controls)); + if (err < 0) + return err; + } if (chip->has_ac97_0) { err = add_controls(chip, ac97_controls, ARRAY_SIZE(ac97_controls)); diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index b70046aca65..b17c405e069 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -119,7 +119,7 @@ static int oxygen_open(struct snd_pcm_substream *substream, runtime->private_data = (void *)(uintptr_t)channel; if (channel == PCM_B && chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1)) runtime->hw = oxygen_ac97_hardware; else runtime->hw = *oxygen_hardware[channel]; @@ -365,7 +365,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, return err; is_ac97 = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); spin_lock_irq(&chip->reg_lock); oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, @@ -640,34 +640,39 @@ int oxygen_pcm_init(struct oxygen *chip) int outs, ins; int err; - outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ - ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)); - err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); - if (err < 0) - return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); - if (chip->model->used_channels & OXYGEN_CHANNEL_A) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_a_ops); - else if (chip->model->used_channels & OXYGEN_CHANNEL_B) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &oxygen_rec_b_ops); - pcm->private_data = chip; - pcm->private_free = oxygen_pcm_free; - strcpy(pcm->name, "Analog"); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 512 * 1024, 2048 * 1024); - if (ins) - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - 128 * 1024, 256 * 1024); - - outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); - ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S); + ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 | + CAPTURE_0_FROM_I2S_2)); + if (outs | ins) { + err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_multich_ops); + if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_a_ops); + else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Analog"); + if (outs) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 512 * 1024, 2048 * 1024); + if (ins) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF); if (outs | ins) { err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); if (err < 0) @@ -686,12 +691,13 @@ int oxygen_pcm_init(struct oxygen *chip) 128 * 1024, 256 * 1024); } - outs = chip->has_ac97_1 && - (chip->model->used_channels & OXYGEN_CHANNEL_AC97); - ins = outs || - (chip->model->used_channels & (OXYGEN_CHANNEL_A | - OXYGEN_CHANNEL_B)) - == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); + if (chip->has_ac97_1) { + outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1); + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); + } else { + outs = 0; + ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2); + } if (outs | ins) { err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", 2, outs, ins, &pcm); diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h new file mode 100644 index 00000000000..698bf46c710 --- /dev/null +++ b/sound/pci/oxygen/pcm1796.h @@ -0,0 +1,58 @@ +#ifndef PCM1796_H_INCLUDED +#define PCM1796_H_INCLUDED + +/* register 16 */ +#define PCM1796_ATL_MASK 0xff +/* register 17 */ +#define PCM1796_ATR_MASK 0xff +/* register 18 */ +#define PCM1796_MUTE 0x01 +#define PCM1796_DME 0x02 +#define PCM1796_DMF_MASK 0x0c +#define PCM1796_DMF_DISABLED 0x00 +#define PCM1796_DMF_48 0x04 +#define PCM1796_DMF_441 0x08 +#define PCM1796_DMF_32 0x0c +#define PCM1796_FMT_MASK 0x70 +#define PCM1796_FMT_16_RJUST 0x00 +#define PCM1796_FMT_20_RJUST 0x10 +#define PCM1796_FMT_24_RJUST 0x20 +#define PCM1796_FMT_24_LJUST 0x30 +#define PCM1796_FMT_16_I2S 0x40 +#define PCM1796_FMT_24_I2S 0x50 +#define PCM1796_ATLD 0x80 +/* register 19 */ +#define PCM1796_INZD 0x01 +#define PCM1796_FLT_MASK 0x02 +#define PCM1796_FLT_SHARP 0x00 +#define PCM1796_FLT_SLOW 0x02 +#define PCM1796_DFMS 0x04 +#define PCM1796_OPE 0x10 +#define PCM1796_ATS_MASK 0x60 +#define PCM1796_ATS_1 0x00 +#define PCM1796_ATS_2 0x20 +#define PCM1796_ATS_4 0x40 +#define PCM1796_ATS_8 0x60 +#define PCM1796_REV 0x80 +/* register 20 */ +#define PCM1796_OS_MASK 0x03 +#define PCM1796_OS_64 0x00 +#define PCM1796_OS_32 0x01 +#define PCM1796_OS_128 0x02 +#define PCM1796_CHSL_MASK 0x04 +#define PCM1796_CHSL_LEFT 0x00 +#define PCM1796_CHSL_RIGHT 0x04 +#define PCM1796_MONO 0x08 +#define PCM1796_DFTH 0x10 +#define PCM1796_DSD 0x20 +#define PCM1796_SRST 0x40 +/* register 21 */ +#define PCM1796_PCMZ 0x01 +#define PCM1796_DZ_MASK 0x06 +/* register 22 */ +#define PCM1796_ZFGL 0x01 +#define PCM1796_ZFGR 0x02 +/* register 23 */ +#define PCM1796_ID_MASK 0x1f + +#endif diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index d163397b85c..7f84fa5deca 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -18,6 +18,9 @@ */ /* + * Xonar D2/D2X + * ------------ + * * CMI8788: * * SPI 0 -> 1st PCM1796 (front) @@ -30,10 +33,33 @@ * GPIO 5 <- external power present (D2X only) * GPIO 7 -> ALT * GPIO 8 -> enable output to speakers + */ + +/* + * Xonar DX + * -------- + * + * CMI8788: + * + * I²C <-> CS4398 (front) + * <-> CS4362A (surround, center/LFE, back) + * + * GPI 0 <- external power present * - * CM9780: + * GPIO 0 -> enable output to speakers + * GPIO 1 -> enable front panel I/O + * GPIO 2 -> M0 of CS5361 + * GPIO 3 -> M1 of CS5361 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) * - * GPIO 0 -> enable AC'97 bypass (line in -> ADC) + * CS4398: + * + * AD0 <- 1 + * AD1 <- 1 + * + * CS4362A: + * + * AD0 <- 0 */ #include <linux/pci.h> @@ -47,11 +73,14 @@ #include <sound/tlv.h> #include "oxygen.h" #include "cm9780.h" +#include "pcm1796.h" +#include "cs4398.h" +#include "cs4362a.h" MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_DESCRIPTION("Asus AV200 driver"); +MODULE_DESCRIPTION("Asus AVx00 driver"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -64,80 +93,44 @@ MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable card"); +enum { + MODEL_D2, + MODEL_D2X, + MODEL_DX, +}; + static struct pci_device_id xonar_ids[] __devinitdata = { - { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */ - { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */ + { OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 }, + { OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX }, + { OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, { } }; MODULE_DEVICE_TABLE(pci, xonar_ids); -#define GPIO_CS5381_M_MASK 0x000c -#define GPIO_CS5381_M_SINGLE 0x0000 -#define GPIO_CS5381_M_DOUBLE 0x0004 -#define GPIO_CS5381_M_QUAD 0x0008 -#define GPIO_EXT_POWER 0x0020 -#define GPIO_ALT 0x0080 -#define GPIO_OUTPUT_ENABLE 0x0100 - -#define GPIO_LINE_MUTE CM9780_GPO0 - -/* register 16 */ -#define PCM1796_ATL_MASK 0xff -/* register 17 */ -#define PCM1796_ATR_MASK 0xff -/* register 18 */ -#define PCM1796_MUTE 0x01 -#define PCM1796_DME 0x02 -#define PCM1796_DMF_MASK 0x0c -#define PCM1796_DMF_DISABLED 0x00 -#define PCM1796_DMF_48 0x04 -#define PCM1796_DMF_441 0x08 -#define PCM1796_DMF_32 0x0c -#define PCM1796_FMT_MASK 0x70 -#define PCM1796_FMT_16_RJUST 0x00 -#define PCM1796_FMT_20_RJUST 0x10 -#define PCM1796_FMT_24_RJUST 0x20 -#define PCM1796_FMT_24_LJUST 0x30 -#define PCM1796_FMT_16_I2S 0x40 -#define PCM1796_FMT_24_I2S 0x50 -#define PCM1796_ATLD 0x80 -/* register 19 */ -#define PCM1796_INZD 0x01 -#define PCM1796_FLT_MASK 0x02 -#define PCM1796_FLT_SHARP 0x00 -#define PCM1796_FLT_SLOW 0x02 -#define PCM1796_DFMS 0x04 -#define PCM1796_OPE 0x10 -#define PCM1796_ATS_MASK 0x60 -#define PCM1796_ATS_1 0x00 -#define PCM1796_ATS_2 0x20 -#define PCM1796_ATS_4 0x40 -#define PCM1796_ATS_8 0x60 -#define PCM1796_REV 0x80 -/* register 20 */ -#define PCM1796_OS_MASK 0x03 -#define PCM1796_OS_64 0x00 -#define PCM1796_OS_32 0x01 -#define PCM1796_OS_128 0x02 -#define PCM1796_CHSL_MASK 0x04 -#define PCM1796_CHSL_LEFT 0x00 -#define PCM1796_CHSL_RIGHT 0x04 -#define PCM1796_MONO 0x08 -#define PCM1796_DFTH 0x10 -#define PCM1796_DSD 0x20 -#define PCM1796_SRST 0x40 -/* register 21 */ -#define PCM1796_PCMZ 0x01 -#define PCM1796_DZ_MASK 0x06 -/* register 22 */ -#define PCM1796_ZFGL 0x01 -#define PCM1796_ZFGR 0x02 -/* register 23 */ -#define PCM1796_ID_MASK 0x1f +#define GPIO_CS53x1_M_MASK 0x000c +#define GPIO_CS53x1_M_SINGLE 0x0000 +#define GPIO_CS53x1_M_DOUBLE 0x0004 +#define GPIO_CS53x1_M_QUAD 0x0008 + +#define GPIO_D2X_EXT_POWER 0x0020 +#define GPIO_D2_ALT 0x0080 +#define GPIO_D2_OUTPUT_ENABLE 0x0100 + +#define GPI_DX_EXT_POWER 0x01 +#define GPIO_DX_OUTPUT_ENABLE 0x0001 +#define GPIO_DX_FRONT_PANEL 0x0002 +#define GPIO_DX_INPUT_ROUTE 0x0100 + +#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */ +#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */ struct xonar_data { - u8 is_d2x; + unsigned int anti_pop_delay; + u16 output_enable_bit; + u8 ext_power_reg; + u8 ext_power_int_reg; + u8 ext_power_bit; u8 has_power; }; @@ -156,62 +149,157 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec, (reg << 8) | value); } -static void xonar_init(struct oxygen *chip) +static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); +} + +static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); +} + +static void xonar_common_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + if (data->ext_power_reg) { + oxygen_set_bits8(chip, data->ext_power_int_reg, + data->ext_power_bit); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); + } + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK); + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + msleep(data->anti_pop_delay); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_d2_init(struct oxygen *chip) { struct xonar_data *data = chip->model_data; unsigned int i; - data->is_d2x = chip->pci->subsystem_device == 0x82b7; + data->anti_pop_delay = 300; + data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE; for (i = 0; i < 4; ++i) { - pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD); + pcm1796_write(chip, i, 18, PCM1796_MUTE | PCM1796_DMF_DISABLED | + PCM1796_FMT_24_LJUST | PCM1796_ATLD); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); pcm1796_write(chip, i, 20, PCM1796_OS_64); pcm1796_write(chip, i, 21, 0); - pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */ - pcm1796_write(chip, i, 17, 0xff); + pcm1796_write(chip, i, 16, 0x0f); /* set ATL/ATR after ATLD */ + pcm1796_write(chip, i, 17, 0x0f); } - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_CS5381_M_MASK | GPIO_ALT); - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - GPIO_CS5381_M_SINGLE, - GPIO_CS5381_M_MASK | GPIO_ALT); - if (data->is_d2x) { - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_EXT_POWER); - oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, - GPIO_EXT_POWER); - chip->interrupt_mask |= OXYGEN_INT_GPIO; - data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) - & GPIO_EXT_POWER); - } - oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); - oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); - msleep(300); - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); + + xonar_common_init(chip); snd_component_add(chip->card, "PCM1796"); snd_component_add(chip->card, "CS5381"); } +static void xonar_d2x_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + data->ext_power_reg = OXYGEN_GPIO_DATA; + data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; + data->ext_power_bit = GPIO_D2X_EXT_POWER; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); + xonar_d2_init(chip); +} + +static void xonar_dx_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + + data->anti_pop_delay = 800; + data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; + data->ext_power_reg = OXYGEN_GPI_DATA; + data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->ext_power_bit = GPI_DX_EXT_POWER; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + /* set CPEN (control port mode) and power down */ + cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + /* configure */ + cs4398_write(chip, 2, CS4398_FM_SINGLE | + CS4398_DEM_NONE | CS4398_DIF_LJUST); + cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + cs4398_write(chip, 4, CS4398_MUTEP_LOW | CS4398_PAMUTE); + cs4398_write(chip, 5, 0xfe); + cs4398_write(chip, 6, 0xfe); + cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | + CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); + cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE | + CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP); + cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); + cs4362a_write(chip, 0x05, 0); + cs4362a_write(chip, 0x06, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x07, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x08, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x09, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x0a, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0b, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0c, CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L); + cs4362a_write(chip, 0x0d, 0x7f | CS4362A_MUTE); + cs4362a_write(chip, 0x0e, 0x7f | CS4362A_MUTE); + /* clear power down */ + cs4398_write(chip, 8, CS4398_CPEN); + cs4362a_write(chip, 0x01, CS4362A_CPEN); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE); + + xonar_common_init(chip); + + snd_component_add(chip->card, "CS4398"); + snd_component_add(chip->card, "CS4362A"); + snd_component_add(chip->card, "CS5361"); +} + static void xonar_cleanup(struct oxygen *chip) { - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + struct xonar_data *data = chip->model_data; + + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_dx_cleanup(struct oxygen *chip) +{ + xonar_cleanup(chip); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); } static void set_pcm1796_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { -#if 0 unsigned int i; u8 value; value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; for (i = 0; i < 4; ++i) pcm1796_write(chip, i, 20, value); -#endif } static void update_pcm1796_volume(struct oxygen *chip) @@ -236,19 +324,73 @@ static void update_pcm1796_mute(struct oxygen *chip) pcm1796_write(chip, i, 18, value); } -static void set_cs5381_params(struct oxygen *chip, +static void set_cs53x1_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { unsigned int value; if (params_rate(params) <= 54000) - value = GPIO_CS5381_M_SINGLE; + value = GPIO_CS53x1_M_SINGLE; else if (params_rate(params) <= 108000) - value = GPIO_CS5381_M_DOUBLE; + value = GPIO_CS53x1_M_DOUBLE; else - value = GPIO_CS5381_M_QUAD; + value = GPIO_CS53x1_M_QUAD; oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - value, GPIO_CS5381_M_MASK); + value, GPIO_CS53x1_M_MASK); +} + +static void set_cs43xx_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + u8 fm_cs4398, fm_cs4362a; + + fm_cs4398 = CS4398_DEM_NONE | CS4398_DIF_LJUST; + fm_cs4362a = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + if (params_rate(params) <= 50000) { + fm_cs4398 |= CS4398_FM_SINGLE; + fm_cs4362a |= CS4362A_FM_SINGLE; + } else if (params_rate(params) <= 100000) { + fm_cs4398 |= CS4398_FM_DOUBLE; + fm_cs4362a |= CS4362A_FM_DOUBLE; + } else { + fm_cs4398 |= CS4398_FM_QUAD; + fm_cs4362a |= CS4362A_FM_QUAD; + } + cs4398_write(chip, 2, fm_cs4398); + cs4362a_write(chip, 0x06, fm_cs4362a); + cs4362a_write(chip, 0x09, fm_cs4362a); + cs4362a_write(chip, 0x0c, fm_cs4362a); +} + +static void update_cs4362a_volumes(struct oxygen *chip) +{ + u8 mute; + + mute = chip->dac_mute ? CS4362A_MUTE : 0; + cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute); + cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute); + cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute); + cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute); + cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute); + cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute); +} + +static void update_cs43xx_volume(struct oxygen *chip) +{ + cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); + cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); + update_cs4362a_volumes(chip); +} + +static void update_cs43xx_mute(struct oxygen *chip) +{ + u8 reg; + + reg = CS4398_MUTEP_LOW | CS4398_PAMUTE; + if (chip->dac_mute) + reg |= CS4398_MUTE_B | CS4398_MUTE_A; + cs4398_write(chip, 4, reg); + update_cs4362a_volumes(chip); } static void xonar_gpio_changed(struct oxygen *chip) @@ -256,10 +398,8 @@ static void xonar_gpio_changed(struct oxygen *chip) struct xonar_data *data = chip->model_data; u8 has_power; - if (!data->is_d2x) - return; - has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) - & GPIO_EXT_POWER); + has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); if (has_power != data->has_power) { data->has_power = has_power; if (has_power) { @@ -272,66 +412,13 @@ static void xonar_gpio_changed(struct oxygen *chip) } } -static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) -{ - unsigned int index = chip->controls[control]->private_value & 0xff; - u16 value; - - value = oxygen_read_ac97(chip, 0, index); - if (!(value & 0x8000)) { - oxygen_write_ac97(chip, 0, index, value | 0x8000); - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->controls[control]->id); - } -} - -static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec, - unsigned int reg, int mute) -{ - if (codec != 0) - return; - /* line-in is exclusive */ - switch (reg) { - case AC97_LINE: - oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, - mute ? GPIO_LINE_MUTE : 0, - GPIO_LINE_MUTE); - if (!mute) { - mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); - mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); - } - break; - case AC97_MIC: - case AC97_CD: - case AC97_VIDEO: - case AC97_AUX: - if (!mute) { - oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, - GPIO_LINE_MUTE); - mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); - } - break; - } -} - -static int pcm1796_volume_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 8; - info->value.integer.min = 0x0f; - info->value.integer.max = 0xff; - return 0; -} - static int alt_switch_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; value->value.integer.value[0] = - !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT); + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT); return 0; } @@ -345,9 +432,9 @@ static int alt_switch_put(struct snd_kcontrol *ctl, spin_lock_irq(&chip->reg_lock); old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); if (value->value.integer.value[0]) - new_bits = old_bits | GPIO_ALT; + new_bits = old_bits | GPIO_D2_ALT; else - new_bits = old_bits & ~GPIO_ALT; + new_bits = old_bits & ~GPIO_D2_ALT; changed = new_bits != old_bits; if (changed) oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); @@ -363,20 +450,68 @@ static const struct snd_kcontrol_new alt_switch = { .put = alt_switch_put, }; +static int front_panel_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL); + return 0; +} + +static int front_panel_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 old_reg, new_reg; + + spin_lock_irq(&chip->reg_lock); + old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_reg = old_reg | GPIO_DX_FRONT_PANEL; + else + new_reg = old_reg & ~GPIO_DX_FRONT_PANEL; + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); + spin_unlock_irq(&chip->reg_lock); + return old_reg != new_reg; +} + +static const struct snd_kcontrol_new front_panel_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Panel Switch", + .info = snd_ctl_boolean_mono_info, + .get = front_panel_get, + .put = front_panel_put, +}; + +static void xonar_dx_ac97_switch(struct oxygen *chip, + unsigned int reg, unsigned int mute) +{ + if (reg == AC97_LINE) { + spin_lock_irq(&chip->reg_lock); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + mute ? GPIO_DX_INPUT_ROUTE : 0, + GPIO_DX_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); +static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -12700, 100, 0); -static int xonar_control_filter(struct snd_kcontrol_new *template) +static int xonar_d2_control_filter(struct snd_kcontrol_new *template) { - if (!strcmp(template->name, "Master Playback Volume")) { - template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - template->info = pcm1796_volume_info, - template->tlv.p = pcm1796_db_scale; - } else if (!strncmp(template->name, "CD Capture ", 11)) { + if (!strncmp(template->name, "CD Capture ", 11)) /* CD in is actually connected to the video in pin */ template->private_value ^= AC97_CD ^ AC97_VIDEO; - } else if (!strcmp(template->name, "Line Capture Volume")) { - return 1; /* line-in bypasses the AC'97 mixer */ - } + return 0; +} + +static int xonar_dx_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ return 0; } @@ -385,30 +520,96 @@ static int xonar_mixer_init(struct oxygen *chip) return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); } -static const struct oxygen_model model_xonar = { - .shortname = "Asus AV200", - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .owner = THIS_MODULE, - .init = xonar_init, - .control_filter = xonar_control_filter, - .mixer_init = xonar_mixer_init, - .cleanup = xonar_cleanup, - .set_dac_params = set_pcm1796_params, - .set_adc_params = set_cs5381_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .ac97_switch_hook = xonar_ac97_switch_hook, - .gpio_changed = xonar_gpio_changed, - .model_data_size = sizeof(struct xonar_data), - .dac_channels = 8, - .used_channels = OXYGEN_CHANNEL_B | - OXYGEN_CHANNEL_C | - OXYGEN_CHANNEL_SPDIF | - OXYGEN_CHANNEL_MULTICH, - .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +static int xonar_dx_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); +} + +static const struct oxygen_model xonar_models[] = { + [MODEL_D2] = { + .shortname = "Xonar D2", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_d2_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 0x0f, + .dac_volume_max = 0xff, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, + [MODEL_D2X] = { + .shortname = "Xonar D2X", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_d2x_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .gpio_changed = xonar_gpio_changed, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 0x0f, + .dac_volume_max = 0xff, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_SPI | + OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, + [MODEL_DX] = { + .shortname = "Xonar DX", + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .owner = THIS_MODULE, + .init = xonar_dx_init, + .control_filter = xonar_dx_control_filter, + .mixer_init = xonar_dx_mixer_init, + .cleanup = xonar_dx_cleanup, + .set_dac_params = set_cs43xx_params, + .set_adc_params = set_cs53x1_params, + .update_dac_volume = update_cs43xx_volume, + .update_dac_mute = update_cs43xx_mute, + .gpio_changed = xonar_gpio_changed, + .ac97_switch = xonar_dx_ac97_switch, + .dac_tlv = cs4362a_db_scale, + .model_data_size = sizeof(struct xonar_data), + .pcm_dev_cfg = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 8, + .dac_volume_min = 0, + .dac_volume_max = 127, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + }, }; static int __devinit xonar_probe(struct pci_dev *pci, @@ -423,7 +624,8 @@ static int __devinit xonar_probe(struct pci_dev *pci, ++dev; return -ENOENT; } - err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar); + err = oxygen_pci_probe(pci, index[dev], id[dev], + &xonar_models[pci_id->driver_data]); if (err >= 0) ++dev; return err; diff --git a/sound/pci/oxygen/wm8785.h b/sound/pci/oxygen/wm8785.h new file mode 100644 index 00000000000..8c23e315ae6 --- /dev/null +++ b/sound/pci/oxygen/wm8785.h @@ -0,0 +1,45 @@ +#ifndef WM8785_H_INCLUDED +#define WM8785_H_INCLUDED + +#define WM8785_R0 0 +#define WM8785_R1 1 +#define WM8785_R2 2 +#define WM8785_R7 7 + +/* R0 */ +#define WM8785_MCR_MASK 0x007 +#define WM8785_MCR_SLAVE 0x000 +#define WM8785_MCR_MASTER_128 0x001 +#define WM8785_MCR_MASTER_192 0x002 +#define WM8785_MCR_MASTER_256 0x003 +#define WM8785_MCR_MASTER_384 0x004 +#define WM8785_MCR_MASTER_512 0x005 +#define WM8785_MCR_MASTER_768 0x006 +#define WM8785_OSR_MASK 0x018 +#define WM8785_OSR_SINGLE 0x000 +#define WM8785_OSR_DOUBLE 0x008 +#define WM8785_OSR_QUAD 0x010 +#define WM8785_FORMAT_MASK 0x060 +#define WM8785_FORMAT_RJUST 0x000 +#define WM8785_FORMAT_LJUST 0x020 +#define WM8785_FORMAT_I2S 0x040 +#define WM8785_FORMAT_DSP 0x060 +/* R1 */ +#define WM8785_WL_MASK 0x003 +#define WM8785_WL_16 0x000 +#define WM8785_WL_20 0x001 +#define WM8785_WL_24 0x002 +#define WM8785_WL_32 0x003 +#define WM8785_LRP 0x004 +#define WM8785_BCLKINV 0x008 +#define WM8785_LRSWAP 0x010 +#define WM8785_DEVNO_MASK 0x0e0 +/* R2 */ +#define WM8785_HPFR 0x001 +#define WM8785_HPFL 0x002 +#define WM8785_SDODIS 0x004 +#define WM8785_PWRDNR 0x008 +#define WM8785_PWRDNL 0x010 +#define WM8785_TDM_MASK 0x1c0 + +#endif diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 9d5bb76229a..7fdcdc8c6b6 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -458,7 +458,7 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", is_capture ? 'c' : 'p', - chip->chip_idx, (void*)subs->runtime->dma_addr, + chip->chip_idx, (void *)(long)subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); @@ -626,7 +626,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif } @@ -846,7 +846,6 @@ static int pcxhr_open(struct snd_pcm_substream *subs) struct pcxhr_mgr *mgr = chip->mgr; struct snd_pcm_runtime *runtime = subs->runtime; struct pcxhr_stream *stream; - int is_capture; mutex_lock(&mgr->setup_mutex); @@ -856,12 +855,10 @@ static int pcxhr_open(struct snd_pcm_substream *subs) if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) { snd_printdd("pcxhr_open playback chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 0; stream = &chip->playback_stream[subs->number]; } else { snd_printdd("pcxhr_open capture chip%d subs%d\n", chip->chip_idx, subs->number); - is_capture = 1; if (mgr->mono_capture) runtime->hw.channels_max = 1; else diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index c4e415d0738..78aa81feaa4 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -897,7 +897,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m #ifdef CONFIG_SND_DEBUG_DETECT do_gettimeofday(&my_tv2); snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n", - my_tv2.tv_usec - my_tv1.tv_usec, err); + (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); #endif return 0; } @@ -1005,30 +1005,37 @@ void pcxhr_msg_tasklet(unsigned long arg) int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD; int pipe = prmh->stat[i] & MASK_FIRST_FIELD; int is_capture = prmh->stat[i] & 0x400000; - u32 err; + u32 err2; if (prmh->stat[i] & 0x800000) { /* if BIT_END */ snd_printdd("TASKLET : End%sPipe %d\n", is_capture ? "Record" : "Play", pipe); } i++; - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_PIPE, + err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_PIPE, pipe, is_capture); i += 2; for (j = 0; j < nb_stream; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_STREAM, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_STREAM, + pipe, + is_capture); i += 2; } for (j = 0; j < nb_audio; j++) { - err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1]; - if (err) - pcxhr_handle_async_err(mgr, err, PCXHR_ERR_AUDIO, - pipe, is_capture); + err2 = prmh->stat[i] ? + prmh->stat[i] : prmh->stat[i+1]; + if (err2) + pcxhr_handle_async_err(mgr, err2, + PCXHR_ERR_AUDIO, + pipe, + is_capture); i += 2; } } diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 9408b1eeec4..979f7da641c 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1630,14 +1630,14 @@ static int snd_riptide_playback_open(struct snd_pcm_substream *substream) struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct pcmhw *data; - int index = substream->number; + int sub_num = substream->number; - chip->playback_substream[index] = substream; + chip->playback_substream[sub_num] = substream; runtime->hw = snd_riptide_playback; data = kzalloc(sizeof(struct pcmhw), GFP_KERNEL); - data->paths = lbus_play_paths[index]; - data->id = play_ids[index]; - data->source = play_sources[index]; + data->paths = lbus_play_paths[sub_num]; + data->id = play_ids[sub_num]; + data->source = play_sources[sub_num]; data->intdec[0] = 0xff; data->intdec[1] = 0xff; data->state = ST_STOP; @@ -1670,10 +1670,10 @@ static int snd_riptide_playback_close(struct snd_pcm_substream *substream) { struct snd_riptide *chip = snd_pcm_substream_chip(substream); struct pcmhw *data = get_pcmhwdev(substream); - int index = substream->number; + int sub_num = substream->number; substream->runtime->private_data = NULL; - chip->playback_substream[index] = NULL; + chip->playback_substream[sub_num] = NULL; kfree(data); return 0; } diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index df184aabce8..e7ef3a1a25a 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1350,7 +1350,8 @@ static int __devinit snd_rme32_create(struct rme32 * rme32) return err; rme32->port = pci_resource_start(rme32->pci, 0); - if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { + rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE); + if (!rme32->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); return -ENOMEM; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index fb0a4ee8bc0..3fdd488d097 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1559,7 +1559,8 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->port = pci_resource_start(rme96->pci, 0); - if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { + rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE); + if (!rme96->iobase) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1be84f22d0d..4d6fbb36ab8 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -318,6 +318,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_midi1IRQPending (1<<31) #define HDSP_spdifFrequencyMask (HDSP_spdifFrequency0|HDSP_spdifFrequency1|HDSP_spdifFrequency2) +#define HDSP_spdifFrequencyMask_9632 (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2|\ + HDSP_spdifFrequency3) #define HDSP_spdifFrequency32KHz (HDSP_spdifFrequency0) #define HDSP_spdifFrequency44_1KHz (HDSP_spdifFrequency1) @@ -328,7 +332,9 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_spdifFrequency96KHz (HDSP_spdifFrequency2|HDSP_spdifFrequency1) /* This is for H9632 cards */ -#define HDSP_spdifFrequency128KHz HDSP_spdifFrequencyMask +#define HDSP_spdifFrequency128KHz (HDSP_spdifFrequency0|\ + HDSP_spdifFrequency1|\ + HDSP_spdifFrequency2) #define HDSP_spdifFrequency176_4KHz HDSP_spdifFrequency3 #define HDSP_spdifFrequency192KHz (HDSP_spdifFrequency3|HDSP_spdifFrequency0) @@ -885,28 +891,15 @@ static int snd_hdsp_use_is_exclusive(struct hdsp *hdsp) return ret; } -static int hdsp_external_sample_rate (struct hdsp *hdsp) -{ - unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); - unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; - - switch (rate_bits) { - case HDSP_systemFrequency32: return 32000; - case HDSP_systemFrequency44_1: return 44100; - case HDSP_systemFrequency48: return 48000; - case HDSP_systemFrequency64: return 64000; - case HDSP_systemFrequency88_2: return 88200; - case HDSP_systemFrequency96: return 96000; - default: - return 0; - } -} - static int hdsp_spdif_sample_rate(struct hdsp *hdsp) { unsigned int status = hdsp_read(hdsp, HDSP_statusRegister); unsigned int rate_bits = (status & HDSP_spdifFrequencyMask); + /* For the 9632, the mask is different */ + if (hdsp->io_type == H9632) + rate_bits = (status & HDSP_spdifFrequencyMask_9632); + if (status & HDSP_SPDIFErrorFlag) return 0; @@ -933,6 +926,31 @@ static int hdsp_spdif_sample_rate(struct hdsp *hdsp) return 0; } +static int hdsp_external_sample_rate(struct hdsp *hdsp) +{ + unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); + unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; + + /* For the 9632 card, there seems to be no bit for indicating external + * sample rate greater than 96kHz. The card reports the corresponding + * single speed. So the best means seems to get spdif rate when + * autosync reference is spdif */ + if (hdsp->io_type == H9632 && + hdsp_autosync_ref(hdsp) == HDSP_AUTOSYNC_FROM_SPDIF) + return hdsp_spdif_sample_rate(hdsp); + + switch (rate_bits) { + case HDSP_systemFrequency32: return 32000; + case HDSP_systemFrequency44_1: return 44100; + case HDSP_systemFrequency48: return 48000; + case HDSP_systemFrequency64: return 64000; + case HDSP_systemFrequency88_2: return 88200; + case HDSP_systemFrequency96: return 96000; + default: + return 0; + } +} + static void hdsp_compute_period_size(struct hdsp *hdsp) { hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8)); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 9a19ae6a64d..ab423bc8234 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -540,7 +540,8 @@ static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, static inline int HDSPM_bit2freq(int n) { - static int bit2freq_tab[] = { 0, 32000, 44100, 48000, 64000, 88200, + static const int bit2freq_tab[] = { + 0, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; if (n < 1 || n > 9) return 0; @@ -582,7 +583,7 @@ static inline int hdspm_read_pb_gain(struct hdspm * hdspm, unsigned int chan, return hdspm->mixer->ch[chan].pb[pb]; } -static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan, +static int hdspm_write_in_gain(struct hdspm *hdspm, unsigned int chan, unsigned int in, unsigned short data) { if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS) @@ -595,7 +596,7 @@ static inline int hdspm_write_in_gain(struct hdspm * hdspm, unsigned int chan, return 0; } -static inline int hdspm_write_pb_gain(struct hdspm * hdspm, unsigned int chan, +static int hdspm_write_pb_gain(struct hdspm *hdspm, unsigned int chan, unsigned int pb, unsigned short data) { if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS) @@ -621,7 +622,7 @@ static inline void snd_hdspm_enable_out(struct hdspm * hdspm, int i, int v) } /* check if same process is writing and reading */ -static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) +static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) { unsigned long flags; int ret = 1; @@ -636,7 +637,7 @@ static inline int snd_hdspm_use_is_exclusive(struct hdspm * hdspm) } /* check for external sample rate */ -static inline int hdspm_external_sample_rate(struct hdspm * hdspm) +static int hdspm_external_sample_rate(struct hdspm *hdspm) { if (hdspm->is_aes32) { unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -787,7 +788,7 @@ static inline void hdspm_stop_audio(struct hdspm * s) } /* should I silence all or only opened ones ? doit all for first even is 4MB*/ -static inline void hdspm_silence_playback(struct hdspm * hdspm) +static void hdspm_silence_playback(struct hdspm *hdspm) { int i; int n = hdspm->period_bytes; @@ -1028,9 +1029,9 @@ static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, { /* the hardware already does the relevant bit-mask with 0xff */ if (id) - return hdspm_write(hdspm, HDSPM_midiDataOut1, val); + hdspm_write(hdspm, HDSPM_midiDataOut1, val); else - return hdspm_write(hdspm, HDSPM_midiDataOut0, val); + hdspm_write(hdspm, HDSPM_midiDataOut0, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) @@ -1057,7 +1058,7 @@ static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) return 0; } -static inline void snd_hdspm_flush_midi_input (struct hdspm *hdspm, int id) +static void snd_hdspm_flush_midi_input(struct hdspm *hdspm, int id) { while (snd_hdspm_midi_input_available (hdspm, id)) snd_hdspm_midi_read_byte (hdspm, id); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 742f1180c39..df2007e3be7 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1194,7 +1194,6 @@ static int sis_suspend(struct pci_dev *pci, pm_message_t state) /* snd_pcm_suspend_all() stopped all channels, so we're quiescent. */ if (sis->irq >= 0) { - synchronize_irq(sis->irq); free_irq(sis->irq, sis); sis->irq = -1; } diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 71138ff9b31..bbcee2c09ae 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3676,6 +3676,8 @@ static int snd_trident_free(struct snd_trident *trident) else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } + if (trident->irq >= 0) + free_irq(trident->irq, trident); if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) @@ -3685,8 +3687,6 @@ static int snd_trident_free(struct snd_trident *trident) vfree(trident->tlb.shadow_entries); snd_dma_free_pages(&trident->tlb.buffer); } - if (trident->irq >= 0) - free_irq(trident->irq, trident); pci_release_regions(trident->pci); pci_disable_device(trident->pci); kfree(trident); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index a756be661f9..b585cc3e4c4 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2236,7 +2236,7 @@ static int snd_via82xx_free(struct via82xx *chip) /* disable interrupts */ for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); __end_hw: diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index f5df1c79bee..31f64ee3988 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1075,7 +1075,7 @@ static int snd_via82xx_free(struct via82xx_modem *chip) /* disable interrupts */ for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); - synchronize_irq(chip->irq); + __end_hw: if (chip->irq >= 0) free_irq(chip->irq, chip); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 42c1eb7d35f..29b3056c510 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2249,6 +2249,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) #ifdef CONFIG_PM vfree(chip->saved_regs); #endif + if (chip->irq >= 0) + free_irq(chip->irq, chip); release_and_free_resource(chip->mpu_res); release_and_free_resource(chip->fm_res); snd_ymfpci_free_gameport(chip); @@ -2257,8 +2259,6 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip) if (chip->work_ptr.area) snd_dma_free_pages(&chip->work_ptr); - if (chip->irq >= 0) - free_irq(chip->irq, chip); release_and_free_resource(chip->res_reg_area); pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 8441e780df0..566a6d0daf4 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -141,7 +141,7 @@ static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 15; return 0; } - + static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -267,7 +267,8 @@ static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol, static void awacs_set_cuda(int reg, int val) { struct adb_request req; - cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); + cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, + reg, val); while (! req.complete) cuda_poll(); } @@ -289,11 +290,11 @@ static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble) /* * vol = 0 - 31 (attenuation), 32 = mute bit, stereo */ -static int awacs_amp_set_vol(struct awacs_amp *amp, int index, int lvol, int rvol, - int do_check) +static int awacs_amp_set_vol(struct awacs_amp *amp, int index, + int lvol, int rvol, int do_check) { if (do_check && amp->amp_vol[index][0] == lvol && - amp->amp_vol[index][1] == rvol) + amp->amp_vol[index][1] == rvol) return 0; awacs_set_cuda(3 + index, lvol); awacs_set_cuda(5 + index, rvol); @@ -337,7 +338,7 @@ static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 31; return 0; } - + static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -361,8 +362,10 @@ static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol, snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); - vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); - vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); + vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) + | (amp->amp_vol[index][0] & 32); + vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) + | (amp->amp_vol[index][1] & 32); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); } @@ -374,8 +377,10 @@ static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol, struct awacs_amp *amp = chip->mixer_data; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); - ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; - ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; + ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) + ? 0 : 1; + ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) + ? 0 : 1; return 0; } @@ -389,8 +394,10 @@ static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol, snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); - vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); - vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); + vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) + | (amp->amp_vol[index][0] & 31); + vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) + | (amp->amp_vol[index][1] & 31); return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); } @@ -403,7 +410,7 @@ static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 14; return 0; } - + static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -445,7 +452,7 @@ static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 99; return 0; } - + static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -544,7 +551,7 @@ static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 2; + uinfo->value.integer.max = 3; return 0; } @@ -552,16 +559,14 @@ static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - int val; + int val = 0; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); if (chip->awacs_reg[6] & MASK_MIC_BOOST) - val = 2; - else if (chip->awacs_reg[0] & MASK_GAINLINE) - val = 1; - else - val = 0; + val |= 2; + if (chip->awacs_reg[0] & MASK_GAINLINE) + val |= 1; spin_unlock_irqrestore(&chip->reg_lock, flags); ucontrol->value.integer.value[0] = val; return 0; @@ -578,11 +583,10 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, spin_lock_irqsave(&chip->reg_lock, flags); val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; - if (ucontrol->value.integer.value[0] > 0) { + if (ucontrol->value.integer.value[0] & 1) val0 |= MASK_GAINLINE; - if (ucontrol->value.integer.value[0] > 1) - val6 |= MASK_MIC_BOOST; - } + if (ucontrol->value.integer.value[0] & 2) + val6 |= MASK_MIC_BOOST; if (val0 != chip->awacs_reg[0]) { snd_pmac_awacs_write_reg(chip, 0, val0); changed = 1; @@ -599,9 +603,32 @@ static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol, * lists of mixer elements */ static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = { - AWACS_VOLUME("Master Playback Volume", 2, 6, 1), AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), - AWACS_VOLUME("Capture Volume", 0, 4, 0), + AWACS_VOLUME("Master Capture Volume", 0, 4, 0), +/* AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */ +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __initdata = { + AWACS_VOLUME("Master Playback Volume", 2, 6, 1), + AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = { + AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), + AWACS_VOLUME("Master Playback Volume", 5, 6, 1), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = { + AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), + AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), +}; + +static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = { + AWACS_VOLUME("Master Playback Volume", 2, 6, 1), AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), }; @@ -621,35 +648,61 @@ static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __initdata = { static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata = AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); +static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __initdata = +AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1); + static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = { - AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0), + AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0), }; static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic Boost", + .name = "Mic Boost Capture Volume", .info = snd_pmac_screamer_mic_boost_info, .get = snd_pmac_screamer_mic_boost_get, .put = snd_pmac_screamer_mic_boost_put, }, }; +static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __initdata = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __initdata = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), + AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0), +}; + +static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __initdata = +{ + AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0), + AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0), +}; + static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = { AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1), }; + static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata = AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac __initdata = +AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0); + /* * add new mixer elements to the card */ -static int build_mixers(struct snd_pmac *chip, int nums, struct snd_kcontrol_new *mixers) +static int build_mixers(struct snd_pmac *chip, int nums, + struct snd_kcontrol_new *mixers) { int i, err; for (i = 0; i < nums; i++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) + err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip)); + if (err < 0) return err; } return 0; @@ -699,8 +752,10 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip) #ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { struct awacs_amp *amp = chip->mixer_data; - awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); - awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); + awacs_amp_set_vol(amp, 0, + amp->amp_vol[0][0], amp->amp_vol[0][1], 0); + awacs_amp_set_vol(amp, 1, + amp->amp_vol[1][0], amp->amp_vol[1][1], 0); awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); awacs_amp_set_master(amp, amp->amp_master); } @@ -708,6 +763,14 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip) } #endif /* CONFIG_PM */ +#define IS_PM7500 (machine_is_compatible("AAPL,7500")) +#define IS_BEIGE (machine_is_compatible("AAPL,Gossamer")) +#define IS_IMAC (machine_is_compatible("PowerMac2,1") \ + || machine_is_compatible("PowerMac2,2") \ + || machine_is_compatible("PowerMac4,1")) + +static int imac; + #ifdef PMAC_SUPPORT_AUTOMUTE /* * auto-mute stuffs @@ -750,9 +813,16 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify) } else #endif { - int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); + int reg = chip->awacs_reg[1] + | (MASK_HDMUTE | MASK_SPKMUTE); + if (imac) { + reg &= ~MASK_SPKMUTE; + reg &= ~MASK_PAROUT1; + } if (snd_pmac_awacs_detect_headphone(chip)) reg &= ~MASK_HDMUTE; + else if (imac) + reg |= MASK_PAROUT1; else reg &= ~MASK_SPKMUTE; if (do_notify && reg == chip->awacs_reg[1]) @@ -778,8 +848,11 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify) int __init snd_pmac_awacs_init(struct snd_pmac *chip) { + int pm7500 = IS_PM7500; + int beige = IS_BEIGE; int err, vol; + imac = IS_IMAC; /* looks like MASK_GAINLINE triggers something, so we set here * as start-up */ @@ -787,7 +860,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip) chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; /* FIXME: Only machines with external SRS module need MASK_PAROUT */ if (chip->has_iic || chip->device_id == 0x5 || - /*chip->_device_id == 0x8 || */ + /* chip->_device_id == 0x8 || */ chip->device_id == 0xb) chip->awacs_reg[1] |= MASK_PAROUT; /* get default volume from nvram */ @@ -798,8 +871,10 @@ snd_pmac_awacs_init(struct snd_pmac *chip) chip->awacs_reg[2] = vol; chip->awacs_reg[4] = vol; if (chip->model == PMAC_SCREAMER) { - chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ - chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ + /* FIXME: screamer has loopthru vol control */ + chip->awacs_reg[5] = vol; + /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ + chip->awacs_reg[6] = MASK_MIC_BOOST; chip->awacs_reg[7] = 0; } @@ -815,7 +890,8 @@ snd_pmac_awacs_init(struct snd_pmac *chip) return -ENOMEM; chip->mixer_data = amp; chip->mixer_free = awacs_amp_free; - awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ + /* mute and zero vol */ + awacs_amp_set_vol(amp, 0, 63, 63, 0); awacs_amp_set_vol(amp, 1, 63, 63, 0); awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ awacs_amp_set_master(amp, 79); /* 0 dB */ @@ -826,20 +902,25 @@ snd_pmac_awacs_init(struct snd_pmac *chip) /* set headphone-jack detection bit */ switch (chip->model) { case PMAC_AWACS: - chip->hp_stat_mask = 0x04; + chip->hp_stat_mask = pm7500 ? MASK_HDPCONN + : MASK_LOCONN; break; case PMAC_SCREAMER: switch (chip->device_id) { case 0x08: - /* 1 = side jack, 2 = front jack */ - chip->hp_stat_mask = 0x03; + case 0x0B: + chip->hp_stat_mask = imac + ? MASK_LOCONN_IMAC | + MASK_HDPLCONN_IMAC | + MASK_HDPRCONN_IMAC + : MASK_HDPCONN; break; case 0x00: case 0x05: - chip->hp_stat_mask = 0x04; + chip->hp_stat_mask = MASK_LOCONN; break; default: - chip->hp_stat_mask = 0x08; + chip->hp_stat_mask = MASK_HDPCONN; break; } break; @@ -854,19 +935,43 @@ snd_pmac_awacs_init(struct snd_pmac *chip) */ strcpy(chip->card->mixername, "PowerMac AWACS"); - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), - snd_pmac_awacs_mixers)) < 0) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), + snd_pmac_awacs_mixers); + if (err < 0) return err; - if (chip->model == PMAC_SCREAMER) + if (beige) + ; + else if (chip->model == PMAC_SCREAMER) err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), snd_pmac_screamer_mixers2); - else + else if (!pm7500) err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), snd_pmac_awacs_mixers2); if (err < 0) return err; - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + if (pm7500) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500), + snd_pmac_awacs_mixers_pmac7500); + else if (beige) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mixers_beige), + snd_pmac_screamer_mixers_beige); + else if (imac) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mixers_imac), + snd_pmac_screamer_mixers_imac); + else + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mixers_pmac), + snd_pmac_awacs_mixers_pmac); + if (err < 0) + return err; + chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac) + ? &snd_pmac_awacs_master_sw_imac + : &snd_pmac_awacs_master_sw, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; #ifdef PMAC_AMP_AVAIL if (chip->mixer_data) { @@ -876,37 +981,58 @@ snd_pmac_awacs_init(struct snd_pmac *chip) * screamer registers. * in this case, it seems the route C is not used. */ - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), - snd_pmac_awacs_amp_vol)) < 0) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), + snd_pmac_awacs_amp_vol); + if (err < 0) return err; /* overwrite */ - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, + chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, + chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; } else #endif /* PMAC_AMP_AVAIL */ { /* route A = headphone, route C = speaker */ - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), - snd_pmac_awacs_speaker_vol)) < 0) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), + snd_pmac_awacs_speaker_vol); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + chip->speaker_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_awacs_speaker_sw_imac + : &snd_pmac_awacs_speaker_sw, chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; } - if (chip->model == PMAC_SCREAMER) { - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), - snd_pmac_screamer_mic_boost)) < 0) - return err; - } else { - if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), - snd_pmac_awacs_mic_boost)) < 0) - return err; - } + if (beige) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige), + snd_pmac_screamer_mic_boost_beige); + else if (imac) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost_imac), + snd_pmac_screamer_mic_boost_imac); + else if (chip->model == PMAC_SCREAMER) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_screamer_mic_boost), + snd_pmac_screamer_mic_boost); + else if (pm7500) + err = build_mixers(chip, + ARRAY_SIZE(snd_pmac_awacs_mic_boost_pmac7500), + snd_pmac_awacs_mic_boost_pmac7500); + else + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), + snd_pmac_awacs_mic_boost); + if (err < 0) + return err; /* * set lowlevel callbacks @@ -917,7 +1043,8 @@ snd_pmac_awacs_init(struct snd_pmac *chip) chip->resume = snd_pmac_awacs_resume; #endif #ifdef PMAC_SUPPORT_AUTOMUTE - if ((err = snd_pmac_add_automute(chip)) < 0) + err = snd_pmac_add_automute(chip); + if (err < 0) return err; chip->detect_headphone = snd_pmac_awacs_detect_headphone; chip->update_automute = snd_pmac_awacs_update_automute; diff --git a/sound/ppc/awacs.h b/sound/ppc/awacs.h index 1b2cc44eda5..c33e6a531cf 100644 --- a/sound/ppc/awacs.h +++ b/sound/ppc/awacs.h @@ -116,6 +116,11 @@ struct awacs_regs { #define MASK_HDMUTE MASK_AMUTE #define SHIFT_HDMUTE 9 #define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ +#define MASK_PAROUT0 (0x1 << 10) /* Parallel Out (???) */ +#define MASK_PAROUT1 (0x1 << 11) /* Parallel Out (enable speaker) */ +#define SHIFT_PAROUT 10 +#define SHIFT_PAROUT0 10 +#define SHIFT_PAROUT1 11 #define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ #define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ @@ -139,7 +144,7 @@ struct awacs_regs { #define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) /* address 6 */ -#define MASK_MIC_BOOST (0x4) /* screamer mic boost */ +#define MASK_MIC_BOOST (0x4) /* screamer mic boost */ #define SHIFT_MIC_BOOST 2 /* Audio Codec Status Reg Bit Masks */ @@ -152,8 +157,15 @@ struct awacs_regs { #define MASK_REVISION (0xf << 12) /* Revision Number */ #define MASK_MFGID (0xf << 8) /* Mfg. ID */ #define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ -#define MASK_INPPORT (0xf) /* Input Port */ -#define MASK_HDPCONN 8 /* headphone plugged in */ +#define MASK_INSENSE (0xf) /* port sense bits: */ +#define MASK_HDPCONN 8 /* headphone plugged in */ +#define MASK_LOCONN 4 /* line-out plugged in */ +#define MASK_LICONN 2 /* line-in plugged in */ +#define MASK_MICCONN 1 /* microphone plugged in */ +#define MASK_LICONN_IMAC 8 /* line-in plugged in */ +#define MASK_HDPRCONN_IMAC 4 /* headphone right plugged in */ +#define MASK_HDPLCONN_IMAC 2 /* headphone left plugged in */ +#define MASK_LOCONN_IMAC 1 /* line-out plugged in */ /* Clipping Count Reg Bit Masks */ /* -------- ----- --- --- ----- */ @@ -163,7 +175,8 @@ struct awacs_regs { /* DBDMA ChannelStatus Bit Masks */ /* ----- ------------- --- ----- */ #define MASK_CSERR (0x1 << 7) /* Error */ -#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ +#define MASK_EOI (0x1 << 6) /* End of Input -- + only for Input Channel */ #define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ #define MASK_WAIT (0x1) /* Wait */ diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 1a545ac0de0..f860d39af36 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -102,7 +102,8 @@ snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr) } static void -snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr, unsigned int val) +snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr, + unsigned int val) { out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); snd_pmac_burgundy_busy_wait(chip); @@ -126,8 +127,11 @@ snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr) return val; } +#define BASE2ADDR(base) ((base) << 12) +#define ADDR2BASE(addr) ((addr) >> 12) + /* - * Burgundy volume: 0 - 100, stereo + * Burgundy volume: 0 - 100, stereo, word reg */ static void snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address, @@ -168,13 +172,6 @@ snd_pmac_burgundy_read_volume(struct snd_pmac *chip, unsigned int address, volume[1] = 0; } - -/* - */ - -#define BASE2ADDR(base) ((base) << 12) -#define ADDR2BASE(addr) ((addr) >> 12) - static int snd_pmac_burgundy_info_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -191,8 +188,8 @@ static int snd_pmac_burgundy_get_volume(struct snd_kcontrol *kcontrol, struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int shift = (kcontrol->private_value >> 8) & 0xff; - snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, - shift); + snd_pmac_burgundy_read_volume(chip, addr, + ucontrol->value.integer.value, shift); return 0; } @@ -204,24 +201,163 @@ static int snd_pmac_burgundy_put_volume(struct snd_kcontrol *kcontrol, int shift = (kcontrol->private_value >> 8) & 0xff; long nvoices[2]; - snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, - shift); + snd_pmac_burgundy_write_volume(chip, addr, + ucontrol->value.integer.value, shift); snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); return (nvoices[0] != ucontrol->value.integer.value[0] || nvoices[1] != ucontrol->value.integer.value[1]); } -#define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ +#define BURGUNDY_VOLUME_W(xname, xindex, addr, shift) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ .info = snd_pmac_burgundy_info_volume,\ .get = snd_pmac_burgundy_get_volume,\ .put = snd_pmac_burgundy_put_volume,\ .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } -/* lineout/speaker */ +/* + * Burgundy volume: 0 - 100, stereo, 2-byte reg + */ +static void +snd_pmac_burgundy_write_volume_2b(struct snd_pmac *chip, unsigned int address, + long *volume, int off) +{ + int lvolume, rvolume; -static int snd_pmac_burgundy_info_switch_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + off |= off << 2; + lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; + rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; + + snd_pmac_burgundy_wcb(chip, address + off, lvolume); + snd_pmac_burgundy_wcb(chip, address + off + 0x500, rvolume); +} + +static void +snd_pmac_burgundy_read_volume_2b(struct snd_pmac *chip, unsigned int address, + long *volume, int off) +{ + volume[0] = snd_pmac_burgundy_rcb(chip, address + off); + if (volume[0] >= BURGUNDY_VOLUME_OFFSET) + volume[0] -= BURGUNDY_VOLUME_OFFSET; + else + volume[0] = 0; + volume[1] = snd_pmac_burgundy_rcb(chip, address + off + 0x100); + if (volume[1] >= BURGUNDY_VOLUME_OFFSET) + volume[1] -= BURGUNDY_VOLUME_OFFSET; + else + volume[1] = 0; +} + +static int snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int off = kcontrol->private_value & 0x300; + snd_pmac_burgundy_read_volume_2b(chip, addr, + ucontrol->value.integer.value, off); + return 0; +} + +static int snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int off = kcontrol->private_value & 0x300; + long nvoices[2]; + + snd_pmac_burgundy_write_volume_2b(chip, addr, + ucontrol->value.integer.value, off); + snd_pmac_burgundy_read_volume_2b(chip, addr, nvoices, off); + return (nvoices[0] != ucontrol->value.integer.value[0] || + nvoices[1] != ucontrol->value.integer.value[1]); +} + +#define BURGUNDY_VOLUME_2B(xname, xindex, addr, off) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_volume_2b,\ + .get = snd_pmac_burgundy_get_volume_2b,\ + .put = snd_pmac_burgundy_put_volume_2b,\ + .private_value = ((ADDR2BASE(addr) & 0xff) | ((off) << 8)) } + +/* + * Burgundy gain/attenuation: 0 - 15, mono/stereo, byte reg + */ +static int snd_pmac_burgundy_info_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int stereo = (kcontrol->private_value >> 24) & 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = stereo + 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 15; + return 0; +} + +static int snd_pmac_burgundy_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int atten = (kcontrol->private_value >> 25) & 1; + int oval; + + oval = snd_pmac_burgundy_rcb(chip, addr); + if (atten) + oval = ~oval & 0xff; + ucontrol->value.integer.value[0] = oval & 0xf; + if (stereo) + ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + return 0; +} + +static int snd_pmac_burgundy_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); + unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + int stereo = (kcontrol->private_value >> 24) & 1; + int atten = (kcontrol->private_value >> 25) & 1; + int oval, val; + + oval = snd_pmac_burgundy_rcb(chip, addr); + if (atten) + oval = ~oval & 0xff; + val = ucontrol->value.integer.value[0]; + if (stereo) + val |= ucontrol->value.integer.value[1] << 4; + else + val |= ucontrol->value.integer.value[0] << 4; + if (atten) + val = ~val & 0xff; + snd_pmac_burgundy_wcb(chip, addr, val); + return val != oval; +} + +#define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ + .info = snd_pmac_burgundy_info_gain,\ + .get = snd_pmac_burgundy_get_gain,\ + .put = snd_pmac_burgundy_put_gain,\ + .private_value = (ADDR2BASE(addr) | ((stereo) << 24) | ((atten) << 25)) } + +/* + * Burgundy switch: 0/1, mono/stereo, word reg + */ +static int snd_pmac_burgundy_info_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { int stereo = (kcontrol->private_value >> 24) & 1; uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; @@ -231,111 +367,207 @@ static int snd_pmac_burgundy_info_switch_out(struct snd_kcontrol *kcontrol, return 0; } -static int snd_pmac_burgundy_get_switch_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_pmac_burgundy_get_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - int lmask = kcontrol->private_value & 0xff; - int rmask = (kcontrol->private_value >> 8) & 0xff; + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = 1 << (kcontrol->private_value & 0xff); + int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; - int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + int val = snd_pmac_burgundy_rcw(chip, addr); ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; if (stereo) ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; return 0; } -static int snd_pmac_burgundy_put_switch_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_pmac_burgundy_put_switch_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - int lmask = kcontrol->private_value & 0xff; - int rmask = (kcontrol->private_value >> 8) & 0xff; + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = 1 << (kcontrol->private_value & 0xff); + int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; int val, oval; - oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - val = oval & ~(lmask | rmask); + oval = snd_pmac_burgundy_rcw(chip, addr); + val = oval & ~(lmask | (stereo ? rmask : 0)); if (ucontrol->value.integer.value[0]) val |= lmask; if (stereo && ucontrol->value.integer.value[1]) val |= rmask; - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); + snd_pmac_burgundy_wcw(chip, addr, val); return val != oval; } -#define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ +#define BURGUNDY_SWITCH_W(xname, xindex, addr, lbit, rbit, stereo) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ - .info = snd_pmac_burgundy_info_switch_out,\ - .get = snd_pmac_burgundy_get_switch_out,\ - .put = snd_pmac_burgundy_put_switch_out,\ - .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } - -/* line/speaker output volume */ -static int snd_pmac_burgundy_info_volume_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + .info = snd_pmac_burgundy_info_switch_w,\ + .get = snd_pmac_burgundy_get_switch_w,\ + .put = snd_pmac_burgundy_put_switch_w,\ + .private_value = ((lbit) | ((rbit) << 8)\ + | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } + +/* + * Burgundy switch: 0/1, mono/stereo, byte reg, bit mask + */ +static int snd_pmac_burgundy_info_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { int stereo = (kcontrol->private_value >> 24) & 1; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = stereo + 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 15; + uinfo->value.integer.max = 1; return 0; } -static int snd_pmac_burgundy_get_volume_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_pmac_burgundy_get_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; int stereo = (kcontrol->private_value >> 24) & 1; - int oval; - - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - ucontrol->value.integer.value[0] = oval & 0xf; + int val = snd_pmac_burgundy_rcb(chip, addr); + ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; if (stereo) - ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; + ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; return 0; } -static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); + unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff); + int lmask = kcontrol->private_value & 0xff; + int rmask = (kcontrol->private_value >> 8) & 0xff; int stereo = (kcontrol->private_value >> 24) & 1; - unsigned int oval, val; - - oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - val = ucontrol->value.integer.value[0] & 15; - if (stereo) - val |= (ucontrol->value.integer.value[1] & 15) << 4; - else - val |= val << 4; - val = ~val & 0xff; + int val, oval; + oval = snd_pmac_burgundy_rcb(chip, addr); + val = oval & ~(lmask | rmask); + if (ucontrol->value.integer.value[0]) + val |= lmask; + if (stereo && ucontrol->value.integer.value[1]) + val |= rmask; snd_pmac_burgundy_wcb(chip, addr, val); return val != oval; } -#define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ +#define BURGUNDY_SWITCH_B(xname, xindex, addr, lmask, rmask, stereo) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ - .info = snd_pmac_burgundy_info_volume_out,\ - .get = snd_pmac_burgundy_get_volume_out,\ - .put = snd_pmac_burgundy_put_volume_out,\ - .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) } + .info = snd_pmac_burgundy_info_switch_b,\ + .get = snd_pmac_burgundy_get_switch_b,\ + .put = snd_pmac_burgundy_put_switch_b,\ + .private_value = ((lmask) | ((rmask) << 8)\ + | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) } +/* + * Burgundy mixers + */ static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __initdata = { - BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), - BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), - BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), - BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), - BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), - /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ - BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), -}; -static struct snd_kcontrol_new snd_pmac_burgundy_master_sw __initdata = -BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); -static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw __initdata = -BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0); + BURGUNDY_VOLUME_W("Master Playback Volume", 0, + MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), + BURGUNDY_VOLUME_W("CD Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLCD, 16), + BURGUNDY_VOLUME_2B("Input Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIX01, 2), + BURGUNDY_VOLUME_2B("Mixer Playback Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIX23, 0), + BURGUNDY_VOLUME_B("CD Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINCD, 1, 0), + BURGUNDY_SWITCH_W("Master Capture Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTENABLES, 24, 0, 0), + BURGUNDY_SWITCH_W("CD Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 0, 16, 1), + BURGUNDY_SWITCH_W("CD Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 0, 16, 1), +/* BURGUNDY_SWITCH_W("Loop Capture Switch", 0, + * MASK_ADDR_BURGUNDY_CAPTURESELECTS, 8, 24, 1), + * BURGUNDY_SWITCH_B("Mixer out Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x02, 0, 0), + * BURGUNDY_SWITCH_B("Mixer Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x01, 0, 0), + * BURGUNDY_SWITCH_B("PCM out Capture Switch", 0, + * MASK_ADDR_BURGUNDY_HOSTIFEH, 0x02, 0, 0), + */ BURGUNDY_SWITCH_B("PCM Capture Switch", 0, + MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0) +}; +static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __initdata = { + BURGUNDY_VOLUME_W("Line in Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLLINE, 16), + BURGUNDY_VOLUME_W("Mic Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINLINE, 1, 0), + BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), + BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), + BURGUNDY_VOLUME_B("Line out Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1), + BURGUNDY_VOLUME_B("Headphone Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENHP, 1, 1), + BURGUNDY_SWITCH_W("Line in Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 1, 17, 1), + BURGUNDY_SWITCH_W("Mic Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), + BURGUNDY_SWITCH_W("Line in Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 1, 17, 1), + BURGUNDY_SWITCH_W("Mic Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), + BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0, + MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) +}; +static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __initdata = { + BURGUNDY_VOLUME_W("Line in Capture Volume", 0, + MASK_ADDR_BURGUNDY_VOLMIC, 16), + BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0, + MASK_ADDR_BURGUNDY_GAINMIC, 1, 0), + BURGUNDY_VOLUME_B("PC Speaker Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1), + BURGUNDY_VOLUME_B("Line out Playback Volume", 0, + MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1), + BURGUNDY_SWITCH_W("Line in Capture Switch", 0, + MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1), + BURGUNDY_SWITCH_W("Line in Playback Switch", 0, + MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1), +/* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0, + * MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */ +}; +static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __initdata = +BURGUNDY_SWITCH_B("Master Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT, + BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __initdata = +BURGUNDY_SWITCH_B("Master Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_INTERN + | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __initdata = +BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __initdata = +BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_INTERN, 0, 0); +static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __initdata = +BURGUNDY_SWITCH_B("Line out Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __initdata = +BURGUNDY_SWITCH_B("Line out Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); +static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __initdata = +BURGUNDY_SWITCH_B("Headphone Playback Switch", 0, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, + BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1); #ifdef PMAC_SUPPORT_AUTOMUTE @@ -350,16 +582,26 @@ static int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip) static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify) { if (chip->auto_mute) { + int imac = machine_is_compatible("iMac"); int reg, oreg; - reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); - reg &= ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_OUTPUT_INTERN); + reg = oreg = snd_pmac_burgundy_rcb(chip, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); + reg &= imac ? ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT + | BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) + : ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT + | BURGUNDY_OUTPUT_INTERN); if (snd_pmac_burgundy_detect_headphone(chip)) - reg |= BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT; + reg |= imac ? (BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT) + : (BURGUNDY_OUTPUT_LEFT + | BURGUNDY_OUTPUT_RIGHT); else - reg |= BURGUNDY_OUTPUT_INTERN; + reg |= imac ? (BURGUNDY_OUTPUT_LEFT + | BURGUNDY_OUTPUT_RIGHT) + : (BURGUNDY_OUTPUT_INTERN); if (do_notify && reg == oreg) return; - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); + snd_pmac_burgundy_wcb(chip, + MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); if (do_notify) { snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_sw_ctl->id); @@ -378,6 +620,7 @@ static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_noti */ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) { + int imac = machine_is_compatible("iMac"); int i, err; /* Checks to see the chip is alive and kicking */ @@ -386,7 +629,7 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) return 1; } - snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, + snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, DEF_BURGUNDY_OUTPUTENABLES); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, DEF_BURGUNDY_MORE_OUTPUTENABLES); @@ -396,7 +639,8 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, DEF_BURGUNDY_INPSEL21); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, - DEF_BURGUNDY_INPSEL3); + imac ? DEF_BURGUNDY_INPSEL3_IMAC + : DEF_BURGUNDY_INPSEL3_PMAC); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, DEF_BURGUNDY_GAINCD); snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, @@ -422,27 +666,62 @@ int __init snd_pmac_burgundy_init(struct snd_pmac *chip) snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, DEF_BURGUNDY_VOLMIC); - if (chip->hp_stat_mask == 0) + if (chip->hp_stat_mask == 0) { /* set headphone-jack detection bit */ - chip->hp_stat_mask = 0x04; - + if (imac) + chip->hp_stat_mask = BURGUNDY_HPDETECT_IMAC_UPPER + | BURGUNDY_HPDETECT_IMAC_LOWER + | BURGUNDY_HPDETECT_IMAC_SIDE; + else + chip->hp_stat_mask = BURGUNDY_HPDETECT_PMAC_BACK; + } /* * build burgundy mixers */ strcpy(chip->card->mixername, "PowerMac Burgundy"); for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) + err = snd_ctl_add(chip->card, + snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip)); + if (err < 0) + return err; + } + for (i = 0; i < (imac ? ARRAY_SIZE(snd_pmac_burgundy_mixers_imac) + : ARRAY_SIZE(snd_pmac_burgundy_mixers_pmac)); i++) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(imac ? &snd_pmac_burgundy_mixers_imac[i] + : &snd_pmac_burgundy_mixers_pmac[i], chip)); + if (err < 0) return err; } - chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_master_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) + chip->master_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_master_sw_imac + : &snd_pmac_burgundy_master_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) + return err; + chip->master_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_line_sw_imac + : &snd_pmac_burgundy_line_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) return err; - chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_speaker_sw, chip); - if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) + if (imac) { + chip->master_sw_ctl = snd_ctl_new1( + &snd_pmac_burgundy_hp_sw_imac, chip); + err = snd_ctl_add(chip->card, chip->master_sw_ctl); + if (err < 0) + return err; + } + chip->speaker_sw_ctl = snd_ctl_new1(imac + ? &snd_pmac_burgundy_speaker_sw_imac + : &snd_pmac_burgundy_speaker_sw_pmac, chip); + err = snd_ctl_add(chip->card, chip->speaker_sw_ctl); + if (err < 0) return err; #ifdef PMAC_SUPPORT_AUTOMUTE - if ((err = snd_pmac_add_automute(chip)) < 0) + err = snd_pmac_add_automute(chip); + if (err < 0) return err; chip->detect_headphone = snd_pmac_burgundy_detect_headphone; diff --git a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h index ebb457a8342..7a7f9cf3d29 100644 --- a/sound/ppc/burgundy.h +++ b/sound/ppc/burgundy.h @@ -22,6 +22,7 @@ #ifndef __BURGUNDY_H #define __BURGUNDY_H +#define MASK_ADDR_BURGUNDY_INPBOOST (0x10 << 12) #define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) #define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) @@ -35,7 +36,10 @@ #define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) #define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) +#define MASK_ADDR_BURGUNDY_CAPTURESELECTS (0x2A << 12) #define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) +#define MASK_ADDR_BURGUNDY_VOLMIX01 (0x2D << 12) +#define MASK_ADDR_BURGUNDY_VOLMIX23 (0x2E << 12) #define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) #define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) @@ -45,6 +49,10 @@ #define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) #define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) #define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) +#define MASK_ADDR_BURGUNDY_ATTENMONO (0x65 << 12) + +#define MASK_ADDR_BURGUNDY_HOSTIFAD (0x78 << 12) +#define MASK_ADDR_BURGUNDY_HOSTIFEH (0x79 << 12) #define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) #define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) @@ -59,21 +67,22 @@ /* These are all default values for the burgundy */ #define DEF_BURGUNDY_INPSEL21 (0xAA) -#define DEF_BURGUNDY_INPSEL3 (0x0A) +#define DEF_BURGUNDY_INPSEL3_IMAC (0x0A) +#define DEF_BURGUNDY_INPSEL3_PMAC (0x05) #define DEF_BURGUNDY_GAINCD (0x33) #define DEF_BURGUNDY_GAINLINE (0x44) #define DEF_BURGUNDY_GAINMIC (0x44) #define DEF_BURGUNDY_GAINMODEM (0x06) -/* Remember: lowest volume here is 0x9b */ +/* Remember: lowest volume here is 0x9B (155) */ #define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) #define DEF_BURGUNDY_VOLLINE (0x00000000) #define DEF_BURGUNDY_VOLMIC (0x00000000) #define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) -#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) -#define DEF_BURGUNDY_OUTPUTENABLES (0x0A) +#define DEF_BURGUNDY_OUTPUTSELECTS (0x010F010F) +#define DEF_BURGUNDY_OUTPUTENABLES (0x0100000A) /* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ #define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) @@ -84,12 +93,22 @@ #define DEF_BURGUNDY_ATTENLINEOUT (0xCC) #define DEF_BURGUNDY_ATTENHP (0xCC) -/* OUTPUTENABLES bits */ +/* MORE_OUTPUTENABLES bits */ #define BURGUNDY_OUTPUT_LEFT 0x02 #define BURGUNDY_OUTPUT_RIGHT 0x04 +#define BURGUNDY_LINEOUT_LEFT 0x08 +#define BURGUNDY_LINEOUT_RIGHT 0x10 +#define BURGUNDY_HP_LEFT 0x20 +#define BURGUNDY_HP_RIGHT 0x40 #define BURGUNDY_OUTPUT_INTERN 0x80 -/* volume offset */ +/* Headphone detection bits */ +#define BURGUNDY_HPDETECT_PMAC_BACK 0x04 +#define BURGUNDY_HPDETECT_IMAC_SIDE 0x04 +#define BURGUNDY_HPDETECT_IMAC_UPPER 0x08 +#define BURGUNDY_HPDETECT_IMAC_LOWER 0x01 + +/* Volume offset */ #define BURGUNDY_VOLUME_OFFSET 155 #endif /* __BURGUNDY_H */ diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 613a565e04d..a38c0c790d2 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -214,7 +214,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, int rate_index; long offset; struct pmac_stream *astr; - + rec->dma_size = snd_pcm_lib_buffer_bytes(subs); rec->period_size = snd_pcm_lib_period_bytes(subs); rec->nperiods = rec->dma_size / rec->period_size; @@ -643,7 +643,7 @@ static int snd_pmac_pcm_close(struct snd_pmac *chip, struct pmac_stream *rec, /* reset constraints */ astr->cur_freqs = chip->freqs_ok; astr->cur_formats = chip->formats_ok; - + return 0; } @@ -1300,9 +1300,9 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) snd_pmac_sound_feature(chip, 1); - /* reset */ - if (chip->model == PMAC_AWACS) - out_le32(&chip->awacs->control, 0x11); + /* reset & enable interrupts */ + if (chip->model <= PMAC_BURGUNDY) + out_le32(&chip->awacs->control, chip->control_mask); /* Powerbooks have odd ways of enabling inputs such as an expansion-bay CD or sound from an internal modem diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 27658521516..a3b51df2bea 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -29,6 +29,7 @@ source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/fsl/Kconfig" +source "sound/soc/davinci/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 4869c9ae7a0..e489dbdde45 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 898a7d36328..3903ab7dfa4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -18,6 +18,10 @@ config SND_SOC_WM9712 tristate depends on SND_SOC +config SND_SOC_WM9713 + tristate + depends on SND_SOC + # Cirrus Logic CS4270 Codec config SND_SOC_CS4270 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c6e5338c266..4e1314c9d3e 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -3,6 +3,7 @@ snd-soc-wm8731-objs := wm8731.o snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm9712-objs := wm9712.o +snd-soc-wm9713-objs := wm9713.o snd-soc-cs4270-objs := cs4270.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o @@ -11,5 +12,6 @@ obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o +obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 242130cf1ab..2a1ffe39690 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -40,7 +40,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream) } #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) struct snd_soc_codec_dai ac97_dai = { .name = "AC97 HiFi", @@ -86,7 +87,7 @@ static int ac97_soc_probe(struct platform_device *pdev) printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION); socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (socdev->codec == NULL) + if (!socdev->codec) return -ENOMEM; codec = socdev->codec; mutex_init(&codec->mutex); @@ -102,17 +103,17 @@ static int ac97_soc_probe(struct platform_device *pdev) /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if(ret < 0) + if (ret < 0) goto err; /* add codec as bus device for standard ac97 */ ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus); - if(ret < 0) + if (ret < 0) goto bus_err; memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97); - if(ret < 0) + if (ret < 0) goto bus_err; ret = snd_soc_register_card(socdev); @@ -135,7 +136,7 @@ static int ac97_soc_remove(struct platform_device *pdev) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; - if(codec == NULL) + if (!codec) return 0; snd_soc_free_pcms(socdev); @@ -145,11 +146,10 @@ static int ac97_soc_remove(struct platform_device *pdev) return 0; } -struct snd_soc_codec_device soc_codec_dev_ac97= { +struct snd_soc_codec_device soc_codec_dev_ac97 = { .probe = ac97_soc_probe, .remove = ac97_soc_remove, }; - EXPORT_SYMBOL_GPL(soc_codec_dev_ac97); MODULE_DESCRIPTION("Soc Generic AC97 driver"); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index bf2ab72d49b..e73fcfd9f5c 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -372,7 +372,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->codec; struct cs4270_private *cs4270 = codec->private_data; - unsigned int ret = 0; + int ret; unsigned int i; unsigned int rate; unsigned int ratio; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 889a897d41a..630684f4a0b 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -660,33 +660,53 @@ struct aic3x_rate_divs { /* AIC3X codec mclk clock divider coefficients */ static const struct aic3x_rate_divs aic3x_divs[] = { /* 8k */ + {12000000, 8000, 48000, 0xa, 16, 3840}, + {19200000, 8000, 48000, 0xa, 10, 2400}, {22579200, 8000, 48000, 0xa, 8, 7075}, {33868800, 8000, 48000, 0xa, 5, 8049}, /* 11.025k */ + {12000000, 11025, 44100, 0x6, 15, 528}, + {19200000, 11025, 44100, 0x6, 9, 4080}, {22579200, 11025, 44100, 0x6, 8, 0}, {33868800, 11025, 44100, 0x6, 5, 3333}, /* 16k */ + {12000000, 16000, 48000, 0x4, 16, 3840}, + {19200000, 16000, 48000, 0x4, 10, 2400}, {22579200, 16000, 48000, 0x4, 8, 7075}, {33868800, 16000, 48000, 0x4, 5, 8049}, /* 22.05k */ + {12000000, 22050, 44100, 0x2, 15, 528}, + {19200000, 22050, 44100, 0x2, 9, 4080}, {22579200, 22050, 44100, 0x2, 8, 0}, {33868800, 22050, 44100, 0x2, 5, 3333}, /* 32k */ + {12000000, 32000, 48000, 0x1, 16, 3840}, + {19200000, 32000, 48000, 0x1, 10, 2400}, {22579200, 32000, 48000, 0x1, 8, 7075}, {33868800, 32000, 48000, 0x1, 5, 8049}, /* 44.1k */ + {12000000, 44100, 44100, 0x0, 15, 528}, + {19200000, 44100, 44100, 0x0, 9, 4080}, {22579200, 44100, 44100, 0x0, 8, 0}, {33868800, 44100, 44100, 0x0, 5, 3333}, /* 48k */ + {12000000, 48000, 48000, 0x0, 16, 3840}, + {19200000, 48000, 48000, 0x0, 10, 2400}, {22579200, 48000, 48000, 0x0, 8, 7075}, {33868800, 48000, 48000, 0x0, 5, 8049}, /* 64k */ + {12000000, 64000, 96000, 0x1, 16, 3840}, + {19200000, 64000, 96000, 0x1, 10, 2400}, {22579200, 64000, 96000, 0x1, 8, 7075}, {33868800, 64000, 96000, 0x1, 5, 8049}, /* 88.2k */ + {12000000, 88200, 88200, 0x0, 15, 528}, + {19200000, 88200, 88200, 0x0, 9, 4080}, {22579200, 88200, 88200, 0x0, 8, 0}, {33868800, 88200, 88200, 0x0, 5, 3333}, /* 96k */ + {12000000, 96000, 96000, 0x0, 16, 3840}, + {19200000, 96000, 96000, 0x0, 10, 2400}, {22579200, 96000, 96000, 0x0, 8, 7075}, {33868800, 96000, 96000, 0x0, 5, 8049}, }; @@ -807,6 +827,8 @@ static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, struct aic3x_priv *aic3x = codec->private_data; switch (freq) { + case 12000000: + case 19200000: case 22579200: case 33868800: aic3x->sysclk = freq; diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9c33fe87492..0cf9265fca8 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -110,7 +110,7 @@ static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg, data[0] = (reg << 1) | ((value >> 8) & 0x0001); data[1] = value & 0x00ff; - wm8731_write_reg_cache (codec, reg, value); + wm8731_write_reg_cache(codec, reg, value); if (codec->hw_write(codec->control_data, data, 2) == 2) return 0; else @@ -154,8 +154,10 @@ static int wm8731_add_controls(struct snd_soc_codec *codec) int err, i; for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) { - if ((err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0) + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8731_snd_controls[i], + codec, NULL)); + if (err < 0) return err; } @@ -221,15 +223,13 @@ static int wm8731_add_widgets(struct snd_soc_codec *codec) { int i; - for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); - } /* set up audio path interconnects */ - for(i = 0; intercon[i][0] != NULL; i++) { + for (i = 0; intercon[i][0] != NULL; i++) snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]); - } snd_soc_dapm_new_widgets(codec); return 0; @@ -589,7 +589,7 @@ pcm_err: static struct snd_soc_device *wm8731_socdev; -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) /* * WM8731 2 wire address is determined by GPIO5 @@ -651,7 +651,7 @@ err: static int wm8731_i2c_detach(struct i2c_client *client) { - struct snd_soc_codec* codec = i2c_get_clientdata(client); + struct snd_soc_codec *codec = i2c_get_clientdata(client); i2c_detach_client(client); kfree(codec->reg_cache); kfree(client); @@ -709,7 +709,7 @@ static int wm8731_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_paths); wm8731_socdev = socdev; -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { normal_i2c[0] = setup->i2c_address; codec->hw_write = (hw_write_t)i2c_master_send; @@ -734,7 +734,7 @@ static int wm8731_remove(struct platform_device *pdev) snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&wm8731_i2c_driver); #endif kfree(codec->private_data); @@ -749,7 +749,6 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = { .suspend = wm8731_suspend, .resume = wm8731_resume, }; - EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731); MODULE_DESCRIPTION("ASoC WM8731 driver"); diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 77a857b997a..16cd5d4d5ad 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -110,7 +110,7 @@ static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg, data[0] = (reg << 1) | ((value >> 8) & 0x0001); data[1] = value & 0x00ff; - wm8750_write_reg_cache (codec, reg, value); + wm8750_write_reg_cache(codec, reg, value); if (codec->hw_write(codec->control_data, data, 2) == 2) return 0; else @@ -257,7 +257,8 @@ static int wm8750_add_controls(struct snd_soc_codec *codec) for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) { err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL)); + snd_soc_cnew(&wm8750_snd_controls[i], + codec, NULL)); if (err < 0) return err; } @@ -478,15 +479,13 @@ static int wm8750_add_widgets(struct snd_soc_codec *codec) { int i; - for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); - } /* set up audio path audio_mapnects */ - for(i = 0; audio_map[i][0] != NULL; i++) { + for (i = 0; audio_map[i][0] != NULL; i++) snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); - } snd_soc_dapm_new_widgets(codec); return 0; @@ -714,8 +713,8 @@ static int wm8750_dapm_event(struct snd_soc_codec *codec, int event) } #define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) #define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) @@ -784,7 +783,8 @@ static int wm8750_resume(struct platform_device *pdev) if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); codec->dapm_state = SNDRV_CTL_POWER_D0; - schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); + schedule_delayed_work(&codec->delayed_work, + msecs_to_jiffies(1000)); } return 0; @@ -864,7 +864,7 @@ pcm_err: around */ static struct snd_soc_device *wm8750_socdev; -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) /* * WM8731 2 wire address is determined by GPIO5 @@ -979,8 +979,8 @@ static int wm8750_probe(struct platform_device *pdev) INIT_LIST_HEAD(&codec->dapm_paths); wm8750_socdev = socdev; INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); - -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) if (setup->i2c_address) { normal_i2c[0] = setup->i2c_address; codec->hw_write = (hw_write_t)i2c_master_send; @@ -1025,7 +1025,7 @@ static int wm8750_remove(struct platform_device *pdev) run_delayed_work(&codec->delayed_work); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&wm8750_i2c_driver); #endif kfree(codec->private_data); @@ -1040,7 +1040,6 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = { .suspend = wm8750_suspend, .resume = wm8750_resume, }; - EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750); MODULE_DESCRIPTION("ASoC WM8750 driver"); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index ddd9c71b3fd..76a5c7b05df 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -198,6 +198,7 @@ static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"}; static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"}; static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC", "Channel Swap"}; +static const char *wm8753_rout2_phase[] = {"Non Inverted", "Inverted"}; static const struct soc_enum wm8753_enum[] = { SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base), @@ -228,6 +229,7 @@ SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter), SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel), SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode), SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel), +SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase), }; @@ -279,7 +281,7 @@ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0 SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1), SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1), -SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1), +SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1), SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0), SOC_ENUM("Bass Boost", wm8753_enum[0]), @@ -330,6 +332,7 @@ SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0), SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai), SOC_ENUM("ADC Data Select", wm8753_enum[27]), +SOC_ENUM("ROUT2 Phase", wm8753_enum[28]), }; /* add non dapm controls */ diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 524f7450804..d2d79e182a4 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -581,22 +581,14 @@ static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) switch (event) { case SNDRV_CTL_POWER_D0: /* full On */ - /* liam - maybe enable thermal shutdown */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff; - ac97_write(codec, AC97_EXTENDED_MID, reg); - break; case SNDRV_CTL_POWER_D1: /* partial On */ case SNDRV_CTL_POWER_D2: /* partial On */ break; case SNDRV_CTL_POWER_D3hot: /* Off, with power */ - /* enable master bias and vmid */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff; - ac97_write(codec, AC97_EXTENDED_MID, reg); ac97_write(codec, AC97_POWERDOWN, 0x0000); break; case SNDRV_CTL_POWER_D3cold: /* Off, without power */ /* disable everything including AC link */ - ac97_write(codec, AC97_EXTENDED_MID, 0xffff); ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); ac97_write(codec, AC97_POWERDOWN, 0xffff); break; diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c new file mode 100644 index 00000000000..1f241161445 --- /dev/null +++ b/sound/soc/codecs/wm9713.c @@ -0,0 +1,1300 @@ +/* + * wm9713.c -- ALSA Soc WM9713 codec support + * + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Revision history + * 4th Feb 2006 Initial version. + * + * Features:- + * + * o Support for AC97 Codec, Voice DAC and Aux DAC + * o Support for DAPM + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "wm9713.h" + +#define WM9713_VERSION "0.15" + +struct wm9713_priv { + u32 pll_in; /* PLL input frequency */ + u32 pll_out; /* PLL output frequency */ +}; + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg); +static int ac97_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val); + +/* + * WM9713 register cache + * Reg 0x3c bit 15 is used by touch driver. + */ +static const u16 wm9713_reg[] = { + 0x6174, 0x8080, 0x8080, 0x8080, + 0xc880, 0xe808, 0xe808, 0x0808, + 0x00da, 0x8000, 0xd600, 0xaaa0, + 0xaaa0, 0xaaa0, 0x0000, 0x0000, + 0x0f0f, 0x0040, 0x0000, 0x7f00, + 0x0405, 0x0410, 0xbb80, 0xbb80, + 0x0000, 0xbb80, 0x0000, 0x4523, + 0x0000, 0x2000, 0x7eff, 0xffff, + 0x0000, 0x0000, 0x0080, 0x0000, + 0x0000, 0x0000, 0xfffe, 0xffff, + 0x0000, 0x0000, 0x0000, 0xfffe, + 0x4000, 0x0000, 0x0000, 0x0000, + 0xb032, 0x3e00, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0006, + 0x0001, 0x0000, 0x574d, 0x4c13, + 0x0000, 0x0000, 0x0000 +}; + +/* virtual HP mixers regs */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 +#define MICB_MUX 0x82 + +static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; +static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *wm9713_rec_src[] = + {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", + "Mono Out", "Zh"}; +static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv", + "Mono Vmid", "Inv Vmid"}; +static const char *wm9713_spk_pga[] = + {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", + "Speaker Vmid", "Inv Vmid"}; +static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone", + "Headphone Vmid"}; +static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; +static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; +static const char *wm9713_dac_inv[] = + {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", + "Headphone Mono", "NC", "Vmid"}; +static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"}; +static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; +static const char *wm9713_micb_select[] = {"MPB", "MPA"}; + +static const struct soc_enum wm9713_enum[] = { +SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/ +SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ +SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ +SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ +SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ +}; + +static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), +SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1), +SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1), +SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), +SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), + +SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), +SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), + +SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", wm9713_enum[5]), +SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), +SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), + +SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1), +SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), +SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", wm9713_enum[6]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", wm9713_enum[17]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), + +SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), +SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), + +SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), +SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1), + +SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), +SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), +SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1), + +SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1), + +SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1), +SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1), +SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1), + +SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1), +SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), +SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), +SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), +SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1), + +SOC_ENUM("Bass Control", wm9713_enum[16]), +SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), +SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), +SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), +}; + +/* add non dapm controls */ +static int wm9713_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm9713_snd_ac97_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + return 0; +} + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path using the current + * register map, thus we add a new (virtual) register to help determine the + * audio route within the device. + */ +static int mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u16 l, r, beep, tone, phone, rec, pcm, aux; + + l = ac97_read(w->codec, HPL_MIXER); + r = ac97_read(w->codec, HPR_MIXER); + beep = ac97_read(w->codec, AC97_PC_BEEP); + tone = ac97_read(w->codec, AC97_MASTER_TONE); + phone = ac97_read(w->codec, AC97_PHONE); + rec = ac97_read(w->codec, AC97_REC_SEL); + pcm = ac97_read(w->codec, AC97_PCM); + aux = ac97_read(w->codec, AC97_AUX); + + if (event & SND_SOC_DAPM_PRE_REG) + return 0; + if ((l & 0x1) || (r & 0x1)) + ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); + else + ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); + + if ((l & 0x2) || (r & 0x2)) + ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); + else + ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); + + if ((l & 0x4) || (r & 0x4)) + ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); + else + ac97_write(w->codec, AC97_PHONE, phone | 0x8000); + + if ((l & 0x8) || (r & 0x8)) + ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); + else + ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); + + if ((l & 0x10) || (r & 0x10)) + ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); + else + ac97_write(w->codec, AC97_PCM, pcm | 0x8000); + + if ((l & 0x20) || (r & 0x20)) + ac97_write(w->codec, AC97_AUX, aux & 0x7fff); + else + ac97_write(w->codec, AC97_AUX, aux | 0x8000); + + return 0; +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), +}; + +/* headphone capture mux */ +static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[1]); + +/* headphone mic mux */ +static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[0]); + +/* Speaker Mixer */ +static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), +SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), +SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), +}; + +/* mono mic mux */ +static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[2]); + +/* mono output mux */ +static const struct snd_kcontrol_new wm9713_mono_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[7]); + +/* speaker left output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[8]); + +/* speaker right output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[9]); + +/* headphone left output mux */ +static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[10]); + +/* headphone right output mux */ +static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[11]); + +/* Out3 mux */ +static const struct snd_kcontrol_new wm9713_out3_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[12]); + +/* Out4 mux */ +static const struct snd_kcontrol_new wm9713_out4_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[13]); + +/* DAC inv mux 1 */ +static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[14]); + +/* DAC inv mux 2 */ +static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[15]); + +/* Capture source left */ +static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[3]); + +/* Capture source right */ +static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[4]); + +/* mic source */ +static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[18]); + +/* mic source B virtual control */ +static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[19]); + +static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_rec_mux_controls), +SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_mic_mux_controls), +SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mic_mux_controls), +SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mux_controls), +SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkl_mux_controls), +SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkr_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out3_mux_controls), +SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out4_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv1_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv2_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcl_mux_controls), +SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcr_mux_controls), +SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, + &wm9713_mic_sel_mux_controls), +SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, + &wm9713_micb_sel_mux_controls), +SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, + &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, + &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), + mixer_event, SND_SOC_DAPM_POST_REG), +SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, + &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, + &wm9713_speaker_mixer_controls[0], + ARRAY_SIZE(wm9713_speaker_mixer_controls)), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1), +SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), +SND_SOC_DAPM_OUTPUT("MONO"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKL"), +SND_SOC_DAPM_OUTPUT("SPKR"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_INPUT("LINEL"), +SND_SOC_DAPM_INPUT("LINER"), +SND_SOC_DAPM_INPUT("MONOIN"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2A"), +SND_SOC_DAPM_INPUT("MIC2B"), +SND_SOC_DAPM_VMID("VMID"), +}; + +static const char *audio_map[][3] = { + /* left HP mixer */ + {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Left HP Mixer", NULL, "Capture Headphone Mux"}, + + /* right HP mixer */ + {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Right HP Mixer", NULL, "Capture Headphone Mux"}, + + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + {"Line Mixer", NULL, "Right Line In"}, + {"Line Mixer", NULL, "Left Line In"}, + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + {"Capture Mixer", NULL, "Left Capture Source"}, + {"Capture Mixer", NULL, "Right Capture Source"}, + + /* speaker mixer */ + {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, + + /* mono mixer */ + {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"}, + {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"}, + {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"}, + {"Mono Mixer", NULL, "Capture Mono Mux"}, + + /* DAC inv mux 1 */ + {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, + + /* DAC inv mux 2 */ + {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, + + /* headphone left mux */ + {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, + + /* headphone right mux */ + {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, + + /* speaker left mux */ + {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, + {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* speaker right mux */ + {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, + {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, + + /* mono mux */ + {"Mono Out Mux", "Mono", "Mono Mixer"}, + {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* out 3 mux */ + {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, + + /* out 4 mux */ + {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Out Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Out Mux"}, + {"OUT3", NULL, "Out 3"}, + {"Out 3", NULL, "Out 3 Mux"}, + {"OUT4", NULL, "Out 4"}, + {"Out 4", NULL, "Out 4 Mux"}, + {"SPKL", NULL, "Left Speaker"}, + {"Left Speaker", NULL, "Left Speaker Out Mux"}, + {"SPKR", NULL, "Right Speaker"}, + {"Right Speaker", NULL, "Right Speaker Out Mux"}, + {"MONO", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono Out Mux"}, + + /* input pga */ + {"Left Line In", NULL, "LINEL"}, + {"Right Line In", NULL, "LINER"}, + {"Mono In", NULL, "MONOIN"}, + {"Mic A PGA", NULL, "Mic A Pre Amp"}, + {"Mic B PGA", NULL, "Mic B Pre Amp"}, + + /* left capture select */ + {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Left Capture Source", "Line", "LINEL"}, + {"Left Capture Source", "Mono In", "MONOIN"}, + {"Left Capture Source", "Headphone", "Left HP Mixer"}, + {"Left Capture Source", "Speaker", "Speaker Mixer"}, + {"Left Capture Source", "Mono Out", "Mono Mixer"}, + + /* right capture select */ + {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Right Capture Source", "Line", "LINER"}, + {"Right Capture Source", "Mono In", "MONOIN"}, + {"Right Capture Source", "Headphone", "Right HP Mixer"}, + {"Right Capture Source", "Speaker", "Speaker Mixer"}, + {"Right Capture Source", "Mono Out", "Mono Mixer"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Source"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Source"}, + + /* mic */ + {"Mic A Pre Amp", NULL, "Mic A Source"}, + {"Mic A Source", "Mic 1", "MIC1"}, + {"Mic A Source", "Mic 2 A", "MIC2A"}, + {"Mic A Source", "Mic 2 B", "Mic B Source"}, + {"Mic B Pre Amp", "MPB", "Mic B Source"}, + {"Mic B Source", NULL, "MIC2B"}, + + /* headphone capture */ + {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, + {"Capture Headphone Mux", "Left", "Left Capture Source"}, + {"Capture Headphone Mux", "Right", "Right Capture Source"}, + + /* mono capture */ + {"Capture Mono Mux", "Stereo", "Capture Mixer"}, + {"Capture Mono Mux", "Left", "Left Capture Source"}, + {"Capture Mono Mux", "Right", "Right Capture Source"}, + + {NULL, NULL, NULL}, +}; + +static int wm9713_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]); + + /* set up audio path audio_mapnects */ + for (i = 0; audio_map[i][0] != NULL; i++) + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static unsigned int ac97_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || + reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || + reg == AC97_CD) + return soc_ac97_ops.read(codec->ac97, reg); + else { + reg = reg >> 1; + + if (reg > (ARRAY_SIZE(wm9713_reg))) + return -EIO; + + return cache[reg]; + } +} + +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + u16 *cache = codec->reg_cache; + if (reg < 0x7c) + soc_ac97_ops.write(codec->ac97, reg, val); + reg = reg >> 1; + if (reg <= (ARRAY_SIZE(wm9713_reg))) + cache[reg] = val; + + return 0; +} + +/* PLL divisors */ +struct _pll_div { + u32 divsel:1; + u32 divctl:1; + u32 lf:1; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the PLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 22) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + + /* The the PLL output is always 98.304MHz. */ + target = 98304000; + + /* If the input frequency is over 14.4MHz then scale it down. */ + if (source > 14400000) { + source >>= 1; + pll_div->divsel = 1; + + if (source > 14400000) { + source >>= 1; + pll_div->divctl = 1; + } else + pll_div->divctl = 0; + + } else { + pll_div->divsel = 0; + pll_div->divctl = 0; + } + + /* Low frequency sources require an additional divide in the + * loop. + */ + if (source < 8192000) { + pll_div->lf = 1; + target >>= 2; + } else + pll_div->lf = 0; + + Ndiv = target / source; + if ((Ndiv < 5) || (Ndiv > 12)) + printk(KERN_WARNING + "WM9713 PLL N value %d out of recommended range!\n", + Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +/** + * Please note that changing the PLL input frequency may require + * resynchronisation with the AC97 controller. + */ +static int wm9713_set_pll(struct snd_soc_codec *codec, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct wm9713_priv *wm9713 = codec->private_data; + u16 reg, reg2; + struct _pll_div pll_div; + + /* turn PLL off ? */ + if (freq_in == 0 || freq_out == 0) { + /* disable PLL power and select ext source */ + reg = ac97_read(codec, AC97_HANDSET_RATE); + ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080); + reg = ac97_read(codec, AC97_EXTENDED_MID); + ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200); + wm9713->pll_out = 0; + return 0; + } + + pll_factors(&pll_div, freq_in); + + if (pll_div.k == 0) { + reg = (pll_div.n << 12) | (pll_div.lf << 11) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + } else { + /* write the fractional k to the reg 0x46 pages */ + reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + + /* K [21:20] */ + reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [19:16] */ + reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [15:12] */ + reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [11:8] */ + reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + /* K [7:4] */ + reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); + ac97_write(codec, AC97_LINE1_LEVEL, reg); + + reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ + ac97_write(codec, AC97_LINE1_LEVEL, reg); + } + + /* turn PLL on and select as source */ + reg = ac97_read(codec, AC97_EXTENDED_MID); + ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff); + reg = ac97_read(codec, AC97_HANDSET_RATE); + ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f); + wm9713->pll_out = freq_out; + wm9713->pll_in = freq_in; + + /* wait 10ms AC97 link frames for the link to stabilise */ + schedule_timeout_interruptible(msecs_to_jiffies(10)); + return 0; +} + +static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = codec_dai->codec; + return wm9713_set_pll(codec, pll_id, freq_in, freq_out); +} + +/* + * Tristate the PCM DAI lines, tristate can be disabled by calling + * wm9713_set_dai_fmt() + */ +static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai, + int tristate) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff; + + if (tristate) + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + + return 0; +} + +/* + * Configure WM9713 clock dividers. + * Voice DAC needs 256 FS + */ +static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + switch (div_id) { + case WM9713_PCMCLK_DIV: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_CLKA_MULT: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_CLKB_MULT: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_HIFI_DIV: + reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff; + ac97_write(codec, AC97_HANDSET_RATE, reg | div); + break; + case WM9713_PCMBCLK_DIV: + reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff; + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div); + break; + case WM9713_PCMCLK_PLL_DIV: + reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; + ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div); + break; + case WM9713_HIFI_PLL_DIV: + reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; + ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5; + u16 reg = 0x8000; + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg |= 0x4000; + gpio |= 0x0010; + break; + case SND_SOC_DAIFMT_CBM_CFS: + reg |= 0x6000; + gpio |= 0x0018; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg |= 0x0200; + gpio |= 0x001a; + break; + case SND_SOC_DAIFMT_CBS_CFM: + gpio |= 0x0012; + break; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + reg |= 0x00c0; + break; + case SND_SOC_DAIFMT_IB_NF: + reg |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + reg |= 0x0040; + break; + } + + /* DAI format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + reg |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + reg |= 0x0043; + break; + } + + ac97_write(codec, AC97_GPIO_CFG, gpio); + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + reg |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + reg |= 0x000c; + break; + } + + /* enable PCM interface in master mode */ + ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static void wm9713_voiceshutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 status; + + /* Gracefully shut down the voice interface. */ + status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; + ac97_write(codec, AC97_HANDSET_RATE, 0x0280); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); + ac97_write(codec, AC97_EXTENDED_MID, status); +} + +static int ac97_hifi_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + int reg; + u16 vra; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return ac97_write(codec, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 vra, xsle; + + vra = ac97_read(codec, AC97_EXTENDED_STATUS); + ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + xsle = ac97_read(codec, AC97_PCI_SID); + ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) + +#define WM9713_PCM_FORMATS \ + (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ + SNDRV_PCM_FORMAT_S24_LE) + +struct snd_soc_codec_dai wm9713_dai[] = { +{ + .name = "AC97 HiFi", + .type = SND_SOC_DAI_AC97_BUS, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .prepare = ac97_hifi_prepare,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll,}, + }, + { + .name = "AC97 Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .prepare = ac97_aux_prepare,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll,}, + }, + { + .name = "WM9713 Voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_RATES, + .formats = WM9713_PCM_FORMATS,}, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = WM9713_PCM_FORMATS,}, + .ops = { + .hw_params = wm9713_pcm_hw_params, + .shutdown = wm9713_voiceshutdown,}, + .dai_ops = { + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll, + .set_fmt = wm9713_set_dai_fmt, + .set_tristate = wm9713_set_dai_tristate, + }, + }, +}; +EXPORT_SYMBOL_GPL(wm9713_dai); + +int wm9713_reset(struct snd_soc_codec *codec, int try_warm) +{ + if (try_warm && soc_ac97_ops.warm_reset) { + soc_ac97_ops.warm_reset(codec->ac97); + if (!(ac97_read(codec, 0) & 0x8000)) + return 1; + } + + soc_ac97_ops.reset(codec->ac97); + if (ac97_read(codec, 0) & 0x8000) + return -EIO; + return 0; +} +EXPORT_SYMBOL_GPL(wm9713_reset); + +static int wm9713_dapm_event(struct snd_soc_codec *codec, int event) +{ + u16 reg; + + switch (event) { + case SNDRV_CTL_POWER_D0: /* full On */ + /* enable thermal shutdown */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + break; + case SNDRV_CTL_POWER_D1: /* partial On */ + case SNDRV_CTL_POWER_D2: /* partial On */ + break; + case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + /* enable master bias and vmid */ + reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; + ac97_write(codec, AC97_EXTENDED_MID, reg); + ac97_write(codec, AC97_POWERDOWN, 0x0000); + break; + case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + /* disable everything including AC link */ + ac97_write(codec, AC97_EXTENDED_MID, 0xffff); + ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + ac97_write(codec, AC97_POWERDOWN, 0xffff); + break; + } + codec->dapm_state = event; + return 0; +} + +static int wm9713_soc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + u16 reg; + + /* Disable everything except touchpanel - that will be handled + * by the touch driver and left disabled if touch is not in + * use. */ + reg = ac97_read(codec, AC97_EXTENDED_MID); + ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff); + ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + ac97_write(codec, AC97_POWERDOWN, 0x6f00); + ac97_write(codec, AC97_POWERDOWN, 0xffff); + + return 0; +} + +static int wm9713_soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + struct wm9713_priv *wm9713 = codec->private_data; + int i, ret; + u16 *cache = codec->reg_cache; + + ret = wm9713_reset(codec, 1); + if (ret < 0) { + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; + } + + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* do we need to re-start the PLL ? */ + if (wm9713->pll_out) + wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out); + + /* only synchronise the codec if warm reset failed */ + if (ret == 0) { + for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || + i == AC97_EXTENDED_MSTATUS || i > 0x66) + continue; + soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); + } + } + + if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0); + + return ret; +} + +static int wm9713_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0, reg; + + printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION); + + socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (socdev->codec == NULL) + return -ENOMEM; + codec = socdev->codec; + mutex_init(&codec->mutex); + + codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto cache_err; + } + codec->reg_cache_size = sizeof(wm9713_reg); + codec->reg_cache_step = 2; + + codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); + if (codec->private_data == NULL) { + ret = -ENOMEM; + goto priv_err; + } + + codec->name = "WM9713"; + codec->owner = THIS_MODULE; + codec->dai = wm9713_dai; + codec->num_dai = ARRAY_SIZE(wm9713_dai); + codec->write = ac97_write; + codec->read = ac97_read; + codec->dapm_event = wm9713_dapm_event; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); + if (ret < 0) + goto codec_err; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) + goto pcm_err; + + /* do a cold reset for the controller and then try + * a warm reset followed by an optional cold reset for codec */ + wm9713_reset(codec, 0); + ret = wm9713_reset(codec, 1); + if (ret < 0) { + printk(KERN_ERR "AC97 link error\n"); + goto reset_err; + } + + wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + /* unmute the adc - move to kcontrol */ + reg = ac97_read(codec, AC97_CD) & 0x7fff; + ac97_write(codec, AC97_CD, reg); + + wm9713_add_controls(codec); + wm9713_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) + goto reset_err; + return 0; + +reset_err: + snd_soc_free_pcms(socdev); + +pcm_err: + snd_soc_free_ac97_codec(codec); + +codec_err: + kfree(codec->private_data); + +priv_err: + kfree(codec->reg_cache); + +cache_err: + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int wm9713_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec == NULL) + return 0; + + snd_soc_dapm_free(socdev); + snd_soc_free_pcms(socdev); + snd_soc_free_ac97_codec(codec); + kfree(codec->private_data); + kfree(codec->reg_cache); + kfree(codec->dai); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm9713 = { + .probe = wm9713_soc_probe, + .remove = wm9713_soc_remove, + .suspend = wm9713_soc_suspend, + .resume = wm9713_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713); + +MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h new file mode 100644 index 00000000000..d357b6c8134 --- /dev/null +++ b/sound/soc/codecs/wm9713.h @@ -0,0 +1,53 @@ +/* + * wm9713.h -- WM9713 Soc Audio driver + */ + +#ifndef _WM9713_H +#define _WM9713_H + +/* clock inputs */ +#define WM9713_CLKA_PIN 0 +#define WM9713_CLKB_PIN 1 + +/* clock divider ID's */ +#define WM9713_PCMCLK_DIV 0 +#define WM9713_CLKA_MULT 1 +#define WM9713_CLKB_MULT 2 +#define WM9713_HIFI_DIV 3 +#define WM9713_PCMBCLK_DIV 4 +#define WM9713_PCMCLK_PLL_DIV 5 +#define WM9713_HIFI_PLL_DIV 6 + +/* Calculate the appropriate bit mask for the external PCM clock divider */ +#define WM9713_PCMDIV(x) ((x - 1) << 8) + +/* Calculate the appropriate bit mask for the external HiFi clock divider */ +#define WM9713_HIFIDIV(x) ((x - 1) << 12) + +/* MCLK clock mulitipliers */ +#define WM9713_CLKA_X1 (0 << 1) +#define WM9713_CLKA_X2 (1 << 1) +#define WM9713_CLKB_X1 (0 << 2) +#define WM9713_CLKB_X2 (1 << 2) + +/* MCLK clock MUX */ +#define WM9713_CLK_MUX_A (0 << 0) +#define WM9713_CLK_MUX_B (1 << 0) + +/* Voice DAI BCLK divider */ +#define WM9713_PCMBCLK_DIV_1 (0 << 9) +#define WM9713_PCMBCLK_DIV_2 (1 << 9) +#define WM9713_PCMBCLK_DIV_4 (2 << 9) +#define WM9713_PCMBCLK_DIV_8 (3 << 9) +#define WM9713_PCMBCLK_DIV_16 (4 << 9) + +#define WM9713_DAI_AC97_HIFI 0 +#define WM9713_DAI_AC97_AUX 1 +#define WM9713_DAI_PCM_VOICE 2 + +extern struct snd_soc_codec_device soc_codec_dev_wm9713; +extern struct snd_soc_codec_dai wm9713_dai[3]; + +int wm9713_reset(struct snd_soc_codec *codec, int try_warm); + +#endif diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig new file mode 100644 index 00000000000..20680c551aa --- /dev/null +++ b/sound/soc/davinci/Kconfig @@ -0,0 +1,19 @@ +config SND_DAVINCI_SOC + tristate "SoC Audio for the TI DAVINCI chip" + depends on ARCH_DAVINCI && SND_SOC + help + Say Y or M if you want to add support for codecs attached to + the DAVINCI AC97 or I2S interface. You will also need + to select the audio interfaces to support below. + +config SND_DAVINCI_SOC_I2S + tristate + +config SND_DAVINCI_SOC_EVM + tristate "SoC Audio support for DaVinci EVM" + depends on SND_DAVINCI_SOC && MACH_DAVINCI_EVM + select SND_DAVINCI_SOC_I2S + select SND_SOC_TLV320AIC3X + help + Say Y if you want to add support for SoC audio on TI + DaVinci EVM platform. diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile new file mode 100644 index 00000000000..ca772e5b463 --- /dev/null +++ b/sound/soc/davinci/Makefile @@ -0,0 +1,11 @@ +# DAVINCI Platform Support +snd-soc-davinci-objs := davinci-pcm.o +snd-soc-davinci-i2s-objs := davinci-i2s.o + +obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o +obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o + +# DAVINCI Machine Support +snd-soc-evm-objs := davinci-evm.o + +obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c new file mode 100644 index 00000000000..fcd16524033 --- /dev/null +++ b/sound/soc/davinci/davinci-evm.c @@ -0,0 +1,208 @@ +/* + * ASoC driver for TI DAVINCI EVM platform + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * 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/module.h> +#include <linux/moduleparam.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <asm/dma.h> +#include <asm/arch/hardware.h> + +#include "../codecs/tlv320aic3x.h" +#include "davinci-pcm.h" +#include "davinci-i2s.h" + +#define EVM_CODEC_CLOCK 22579200 + +static int evm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + int ret = 0; + + /* set codec DAI configuration */ + ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF); + if (ret < 0) + return ret; + + /* set the codec system clock */ + ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, EVM_CODEC_CLOCK, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops evm_ops = { + .hw_params = evm_hw_params, +}; + +/* davinci-evm machine dapm widgets */ +static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +/* davinci-evm machine audio_mapnections to the codec pins */ +static const char *audio_map[][3] = { + /* Headphone connected to HPLOUT, HPROUT */ + {"Headphone Jack", NULL, "HPLOUT"}, + {"Headphone Jack", NULL, "HPROUT"}, + + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LLOUT"}, + {"Line Out", NULL, "RLOUT"}, + + /* Mic connected to (MIC3L | MIC3R) */ + {"MIC3L", NULL, "Mic Bias 2V"}, + {"MIC3R", NULL, "Mic Bias 2V"}, + {"Mic Bias 2V", NULL, "Mic Jack"}, + + /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */ + {"LINE1L", NULL, "Line In"}, + {"LINE2L", NULL, "Line In"}, + {"LINE1R", NULL, "Line In"}, + {"LINE2R", NULL, "Line In"}, + + {NULL, NULL, NULL}, +}; + +/* Logic for a aic3x as connected on a davinci-evm */ +static int evm_aic3x_init(struct snd_soc_codec *codec) +{ + int i; + + /* Add davinci-evm specific widgets */ + for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]); + + /* Set up davinci-evm specific audio path audio_map */ + for (i = 0; audio_map[i][0] != NULL; i++) + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + + /* not connected */ + snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0); + snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0); + snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0); + + /* always connected */ + snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + snd_soc_dapm_set_endpoint(codec, "Line Out", 1); + snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); + snd_soc_dapm_set_endpoint(codec, "Line In", 1); + + snd_soc_dapm_sync_endpoints(codec); + + return 0; +} + +/* davinci-evm digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai = &davinci_i2s_dai, + .codec_dai = &aic3x_dai, + .init = evm_aic3x_init, + .ops = &evm_ops, +}; + +/* davinci-evm audio machine driver */ +static struct snd_soc_machine snd_soc_machine_evm = { + .name = "DaVinci EVM", + .dai_link = &evm_dai, + .num_links = 1, +}; + +/* evm audio private data */ +static struct aic3x_setup_data evm_aic3x_setup = { + .i2c_address = 0x1b, +}; + +/* evm audio subsystem */ +static struct snd_soc_device evm_snd_devdata = { + .machine = &snd_soc_machine_evm, + .platform = &davinci_soc_platform, + .codec_dev = &soc_codec_dev_aic3x, + .codec_data = &evm_aic3x_setup, +}; + +static struct resource evm_snd_resources[] = { + { + .start = DAVINCI_MCBSP_BASE, + .end = DAVINCI_MCBSP_BASE + SZ_8K - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct evm_snd_platform_data evm_snd_data = { + .tx_dma_ch = DM644X_DMACH_MCBSP_TX, + .rx_dma_ch = DM644X_DMACH_MCBSP_RX, +}; + +static struct platform_device *evm_snd_device; + +static int __init evm_init(void) +{ + int ret; + + evm_snd_device = platform_device_alloc("soc-audio", 0); + if (!evm_snd_device) + return -ENOMEM; + + platform_set_drvdata(evm_snd_device, &evm_snd_devdata); + evm_snd_devdata.dev = &evm_snd_device->dev; + evm_snd_device->dev.platform_data = &evm_snd_data; + + ret = platform_device_add_resources(evm_snd_device, evm_snd_resources, + ARRAY_SIZE(evm_snd_resources)); + if (ret) { + platform_device_put(evm_snd_device); + return ret; + } + + ret = platform_device_add(evm_snd_device); + if (ret) + platform_device_put(evm_snd_device); + + return ret; +} + +static void __exit evm_exit(void) +{ + platform_device_unregister(evm_snd_device); +} + +module_init(evm_init); +module_exit(evm_exit); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c new file mode 100644 index 00000000000..c421774b33e --- /dev/null +++ b/sound/soc/davinci/davinci-i2s.c @@ -0,0 +1,407 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * 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/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "davinci-pcm.h" + +#define DAVINCI_MCBSP_DRR_REG 0x00 +#define DAVINCI_MCBSP_DXR_REG 0x04 +#define DAVINCI_MCBSP_SPCR_REG 0x08 +#define DAVINCI_MCBSP_RCR_REG 0x0c +#define DAVINCI_MCBSP_XCR_REG 0x10 +#define DAVINCI_MCBSP_SRGR_REG 0x14 +#define DAVINCI_MCBSP_PCR_REG 0x24 + +#define DAVINCI_MCBSP_SPCR_RRST (1 << 0) +#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4) +#define DAVINCI_MCBSP_SPCR_XRST (1 << 16) +#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20) +#define DAVINCI_MCBSP_SPCR_GRST (1 << 22) +#define DAVINCI_MCBSP_SPCR_FRST (1 << 23) +#define DAVINCI_MCBSP_SPCR_FREE (1 << 25) + +#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) + +#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_XCR_XFIG (1 << 18) +#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) + +#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) +#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) +#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) + +#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) +#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) +#define DAVINCI_MCBSP_PCR_FSRP (1 << 2) +#define DAVINCI_MCBSP_PCR_FSXP (1 << 3) +#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8) +#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9) +#define DAVINCI_MCBSP_PCR_FSRM (1 << 10) +#define DAVINCI_MCBSP_PCR_FSXM (1 << 11) + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) { \ + val |= mask; \ + } else { \ + val &= ~mask; \ + } \ +} while (0) + +enum { + DAVINCI_MCBSP_WORD_8 = 0, + DAVINCI_MCBSP_WORD_12, + DAVINCI_MCBSP_WORD_16, + DAVINCI_MCBSP_WORD_20, + DAVINCI_MCBSP_WORD_24, + DAVINCI_MCBSP_WORD_32, +}; + +static struct davinci_pcm_dma_params davinci_i2s_pcm_out = { + .name = "I2S PCM Stereo out", +}; + +static struct davinci_pcm_dma_params davinci_i2s_pcm_in = { + .name = "I2S PCM Stereo in", +}; + +struct davinci_mcbsp_dev { + void __iomem *base; + struct clk *clk; + struct davinci_pcm_dma_params *dma_params[2]; +}; + +static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, + int reg, u32 val) +{ + __raw_writel(val, dev->base + reg); +} + +static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg) +{ + return __raw_readl(dev->base + reg); +} + +static void davinci_mcbsp_start(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + u32 w; + + /* Start the sample generator and enable transmitter/receiver */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1); + else + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); + + /* Start frame sync */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); +} + +static void davinci_mcbsp_stop(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + u32 w; + + /* Reset transmitter/receiver and sample rate/frame sync generators */ + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST | + DAVINCI_MCBSP_SPCR_FRST, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0); + else + MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w); +} + +static int davinci_i2s_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + + cpu_dai->dma_data = dev->dma_params[substream->stream]; + + return 0; +} + +static int davinci_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) +{ + struct davinci_mcbsp_dev *dev = cpu_dai->private_data; + u32 w; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, + DAVINCI_MCBSP_PCR_FSXM | + DAVINCI_MCBSP_PCR_FSRM | + DAVINCI_MCBSP_PCR_CLKXM | + DAVINCI_MCBSP_PCR_CLKRM); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, + DAVINCI_MCBSP_SRGR_FSGM); + break; + case SND_SOC_DAIFMT_CBM_CFM: + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP | + DAVINCI_MCBSP_PCR_CLKRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_NB_IF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP | + DAVINCI_MCBSP_PCR_FSRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_IB_IF: + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP | + DAVINCI_MCBSP_PCR_CLKRP | + DAVINCI_MCBSP_PCR_FSXP | + DAVINCI_MCBSP_PCR_FSRP, 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w); + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data; + struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; + struct snd_interval *i = NULL; + int mcbsp_word_length; + u32 w; + + /* general line settings */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, + DAVINCI_MCBSP_SPCR_RINTM(3) | + DAVINCI_MCBSP_SPCR_XINTM(3) | + DAVINCI_MCBSP_SPCR_FREE); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, + DAVINCI_MCBSP_RCR_RFRLEN1(1) | + DAVINCI_MCBSP_RCR_RDATDLY(1)); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, + DAVINCI_MCBSP_XCR_XFRLEN1(1) | + DAVINCI_MCBSP_XCR_XDATDLY(1) | + DAVINCI_MCBSP_XCR_XFIG); + + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w); + + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w); + + /* Determine xfer data type */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + dma_params->data_type = 1; + mcbsp_word_length = DAVINCI_MCBSP_WORD_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + dma_params->data_type = 2; + mcbsp_word_length = DAVINCI_MCBSP_WORD_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + dma_params->data_type = 4; + mcbsp_word_length = DAVINCI_MCBSP_WORD_32; + break; + default: + printk(KERN_WARNING "davinci-i2s: unsupported PCM format"); + return -EINVAL; + } + + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w); + + w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG); + MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w); + + return 0; +} + +static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_mcbsp_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_mcbsp_stop(substream); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int davinci_i2s_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai; + struct davinci_mcbsp_dev *dev; + struct resource *mem, *ioarea; + struct evm_snd_platform_data *pdata; + int ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "McBSP region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct davinci_mcbsp_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_release_region; + } + + cpu_dai->private_data = dev; + + dev->clk = clk_get(&pdev->dev, "McBSPCLK"); + if (IS_ERR(dev->clk)) { + ret = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = (void __iomem *)IO_ADDRESS(mem->start); + pdata = pdev->dev.platform_data; + + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr = + (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); + + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr = + (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + + return 0; + +err_free_mem: + kfree(dev); +err_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + return ret; +} + +static void davinci_i2s_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_machine *machine = socdev->machine; + struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai; + struct davinci_mcbsp_dev *dev = cpu_dai->private_data; + struct resource *mem; + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); +} + +#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 + +struct snd_soc_cpu_dai davinci_i2s_dai = { + .name = "davinci-i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .probe = davinci_i2s_probe, + .remove = davinci_i2s_remove, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .startup = davinci_i2s_startup, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params,}, + .dai_ops = { + .set_fmt = davinci_i2s_set_dai_fmt, + }, +}; +EXPORT_SYMBOL_GPL(davinci_i2s_dai); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h new file mode 100644 index 00000000000..9592d17db32 --- /dev/null +++ b/sound/soc/davinci/davinci-i2s.h @@ -0,0 +1,17 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DAVINCI_I2S_H +#define _DAVINCI_I2S_H + +extern struct snd_soc_cpu_dai davinci_i2s_dai; + +#endif diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c new file mode 100644 index 00000000000..6a76927c997 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm.c @@ -0,0 +1,389 @@ +/* + * ALSA PCM interface for the TI DAVINCI processor + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * 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/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/dma.h> + +#include "davinci-pcm.h" + +#define DAVINCI_PCM_DEBUG 0 +#if DAVINCI_PCM_DEBUG +#define DPRINTK(x...) printk(KERN_DEBUG x) +#else +#define DPRINTK(x...) +#endif + +static struct snd_pcm_hardware davinci_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +struct davinci_runtime_data { + spinlock_t lock; + int period; /* current DMA period */ + int master_lch; /* Master DMA channel */ + int slave_lch; /* Slave DMA channel */ + struct davinci_pcm_dma_params *params; /* DMA params */ +}; + +static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int lch = prtd->slave_lch; + unsigned int period_size; + unsigned int dma_offset; + dma_addr_t dma_pos; + dma_addr_t src, dst; + unsigned short src_bidx, dst_bidx; + unsigned int data_type; + unsigned int count; + + period_size = snd_pcm_lib_period_bytes(substream); + dma_offset = prtd->period * period_size; + dma_pos = runtime->dma_addr + dma_offset; + + DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x " + "period_size=%x\n", lch, dma_pos, period_size); + + data_type = prtd->params->data_type; + count = period_size / data_type; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = dma_pos; + dst = prtd->params->dma_addr; + src_bidx = data_type; + dst_bidx = 0; + } else { + src = prtd->params->dma_addr; + dst = dma_pos; + src_bidx = 0; + dst_bidx = data_type; + } + + davinci_set_dma_src_params(lch, src, INCR, W8BIT); + davinci_set_dma_dest_params(lch, dst, INCR, W8BIT); + davinci_set_dma_src_index(lch, src_bidx, 0); + davinci_set_dma_dest_index(lch, dst_bidx, 0); + davinci_set_dma_transfer_params(lch, data_type, count, 1, 0, ASYNC); + + prtd->period++; + if (unlikely(prtd->period >= runtime->periods)) + prtd->period = 0; +} + +static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data) +{ + struct snd_pcm_substream *substream = data; + struct davinci_runtime_data *prtd = substream->runtime->private_data; + + DPRINTK("lch=%d, status=0x%x\n", lch, ch_status); + + if (unlikely(ch_status != DMA_COMPLETE)) + return; + + if (snd_pcm_running(substream)) { + snd_pcm_period_elapsed(substream); + + spin_lock(&prtd->lock); + davinci_pcm_enqueue_dma(substream); + spin_unlock(&prtd->lock); + } +} + +static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; + int tcc = TCC_ANY; + int ret; + + if (!dma_data) + return -ENODEV; + + prtd->params = dma_data; + + /* Request master DMA channel */ + ret = davinci_request_dma(prtd->params->channel, prtd->params->name, + davinci_pcm_dma_irq, substream, + &prtd->master_lch, &tcc, EVENTQ_0); + if (ret) + return ret; + + /* Request slave DMA channel */ + ret = davinci_request_dma(PARAM_ANY, "Link", + NULL, NULL, &prtd->slave_lch, &tcc, EVENTQ_0); + if (ret) { + davinci_free_dma(prtd->master_lch); + return ret; + } + + /* Link slave DMA channel in loopback */ + davinci_dma_link_lch(prtd->slave_lch, prtd->slave_lch); + + return 0; +} + +static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + int ret = 0; + + spin_lock(&prtd->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_start_dma(prtd->master_lch); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_stop_dma(prtd->master_lch); + break; + default: + ret = -EINVAL; + break; + } + + spin_unlock(&prtd->lock); + + return ret; +} + +static int davinci_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct davinci_runtime_data *prtd = substream->runtime->private_data; + struct paramentry_descriptor temp; + + prtd->period = 0; + davinci_pcm_enqueue_dma(substream); + + /* Get slave channel dma params for master channel startup */ + davinci_get_dma_params(prtd->slave_lch, &temp); + davinci_set_dma_params(prtd->master_lch, &temp); + + return 0; +} + +static snd_pcm_uframes_t +davinci_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + unsigned int offset; + dma_addr_t count; + dma_addr_t src, dst; + + spin_lock(&prtd->lock); + + davinci_dma_getposition(prtd->master_lch, &src, &dst); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + count = src - runtime->dma_addr; + else + count = dst - runtime->dma_addr;; + + spin_unlock(&prtd->lock); + + offset = bytes_to_frames(runtime, count); + if (offset >= runtime->buffer_size) + offset = 0; + + return offset; +} + +static int davinci_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); + + prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + spin_lock_init(&prtd->lock); + + runtime->private_data = prtd; + + ret = davinci_pcm_dma_request(substream); + if (ret) { + printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); + kfree(prtd); + } + + return ret; +} + +static int davinci_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd = runtime->private_data; + + davinci_dma_unlink_lch(prtd->slave_lch, prtd->slave_lch); + + davinci_free_dma(prtd->slave_lch); + davinci_free_dma(prtd->master_lch); + + kfree(prtd); + + return 0; +} + +static int davinci_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)); +} + +static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int davinci_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +struct snd_pcm_ops davinci_pcm_ops = { + .open = davinci_pcm_open, + .close = davinci_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = davinci_pcm_hw_params, + .hw_free = davinci_pcm_hw_free, + .prepare = davinci_pcm_prepare, + .trigger = davinci_pcm_trigger, + .pointer = davinci_pcm_pointer, + .mmap = davinci_pcm_mmap, +}; + +static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = davinci_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + + DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", + (void *) buf->area, (void *) buf->addr, size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static void davinci_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static u64 davinci_pcm_dmamask = 0xffffffff; + +static int davinci_pcm_new(struct snd_card *card, + struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) +{ + int ret; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &davinci_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + ret = davinci_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + return ret; + } + + if (dai->capture.channels_min) { + ret = davinci_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + } + + return 0; +} + +struct snd_soc_platform davinci_soc_platform = { + .name = "davinci-audio", + .pcm_ops = &davinci_pcm_ops, + .pcm_new = davinci_pcm_new, + .pcm_free = davinci_pcm_free, +}; +EXPORT_SYMBOL_GPL(davinci_soc_platform); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h new file mode 100644 index 00000000000..8d6a45e75a6 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm.h @@ -0,0 +1,29 @@ +/* + * ALSA PCM interface for the TI DAVINCI processor + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DAVINCI_PCM_H +#define _DAVINCI_PCM_H + +struct davinci_pcm_dma_params { + char *name; /* stream identifier */ + int channel; /* sync dma channel ID */ + dma_addr_t dma_addr; /* device physical address for DMA */ + unsigned int data_type; /* xfer data type */ +}; + +struct evm_snd_platform_data { + int tx_dma_ch; + int rx_dma_ch; +}; + +extern struct snd_soc_platform davinci_soc_platform; + +#endif diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 652514fc814..78de7168d2b 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 145ad13d52d..b2a11b0d2e4 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -15,7 +15,6 @@ #include <linux/device.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 1a70a6ac98c..7f32a116757 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -297,21 +297,19 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) /* Add corgi specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL)); + snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL)); if (err < 0) return err; } /* Add corgi specific widgets */ - for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); - } /* Set up corgi specific audio path audio_map */ - for(i = 0; audio_map[i][0] != NULL; i++) { + for (i = 0; audio_map[i][0] != NULL; i++) snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); - } snd_soc_dapm_sync_endpoints(codec); return 0; @@ -353,7 +351,8 @@ static int __init corgi_init(void) { int ret; - if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky())) + if (!(machine_is_corgi() || machine_is_shepherd() || + machine_is_husky())) return -ENODEV; corgi_snd_device = platform_device_alloc("soc-audio", -1); diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 4fbf8bba962..7e830b21894 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -257,21 +257,19 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) /* Add poodle specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL)); + snd_soc_cnew(&wm8731_poodle_controls[i], codec, NULL)); if (err < 0) return err; } /* Add poodle specific widgets */ - for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]); - } /* Set up poodle specific audio path audio_map */ - for (i = 0; audio_map[i][0] != NULL; i++) { + for (i = 0; audio_map[i][0] != NULL; i++) snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); - } snd_soc_dapm_sync_endpoints(codec); return 0; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index e1737999880..97ec2d90547 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -61,7 +61,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, mutex_lock(&car_mutex); /* set up primary or secondary codec/modem space */ -#ifdef CONFIG_PXA27x +#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; #else if (reg == AC97_GPIO_STATUS) @@ -87,7 +87,7 @@ static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); if (!((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); val = -1; goto out; } @@ -111,7 +111,7 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, mutex_lock(&car_mutex); /* set up primary or secondary codec/modem space */ -#ifdef CONFIG_PXA27x +#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; #else if (reg == AC97_GPIO_STATUS) @@ -127,13 +127,16 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); if (!((GSR | gsr_bits) & GSR_CDONE)) printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", - __FUNCTION__, reg, GSR | gsr_bits); + __func__, reg, GSR | gsr_bits); mutex_unlock(&car_mutex); } static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) { +#ifdef CONFIG_PXA3xx + int timeout = 100; +#endif gsr_bits = 0; #ifdef CONFIG_PXA27x @@ -144,6 +147,11 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) GCR |= GCR_WARM_RST; pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); udelay(500); +#elif defined(CONFIG_PXA3xx) + /* 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); @@ -151,7 +159,7 @@ static void pxa2xx_ac97_warm_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); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; @@ -159,6 +167,16 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) { +#ifdef CONFIG_PXA3xx + int timeout = 1000; + + /* 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 */ @@ -170,6 +188,13 @@ static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) clk_disable(ac97conf_clk); GCR = GCR_COLD_RST; udelay(50); +#elif defined(CONFIG_PXA3xx) + /* 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; @@ -178,7 +203,7 @@ static void pxa2xx_ac97_cold_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); GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index daeaa4c8b87..01ad7bf716b 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -64,8 +64,8 @@ static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - prtd->params->name, dma_ch, dcsr ); + printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", + prtd->params->name, dma_ch, dcsr); } } @@ -84,8 +84,8 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma) - return 0; + if (!dma) + return 0; /* this may get called several times by oss emulation * with different params */ @@ -363,7 +363,6 @@ struct snd_soc_platform pxa2xx_soc_platform = { .pcm_new = pxa2xx_pcm_new, .pcm_free = pxa2xx_pcm_free_dma_buffers, }; - EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); MODULE_AUTHOR("Nicolas Pitre"); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index ecca39033fc..d8b8372db00 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -313,15 +313,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) } /* Add spitz specific widgets */ - for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) { + for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]); - } /* Set up spitz specific audio path audio_map */ - for (i = 0; audio_map[i][0] != NULL; i++) { + for (i = 0; audio_map[i][0] != NULL; i++) snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]); - } snd_soc_dapm_sync_endpoints(codec); return 0; diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 6ee115ceb01..962cc20b1af 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -659,6 +659,7 @@ static int __init neo1973_init(void) static void __exit neo1973_exit(void) { + i2c_del_driver(&lm4857_i2c_driver); platform_device_unregister(neo1973_snd_device); } diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 0a3c630951b..4ebcd6a8bf2 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -25,6 +25,7 @@ #include <linux/delay.h> #include <linux/clk.h> #include <linux/jiffies.h> +#include <linux/io.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -32,7 +33,6 @@ #include <sound/soc.h> #include <asm/hardware.h> -#include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-clock.h> #include <asm/arch/audio.h> @@ -46,7 +46,7 @@ #define S3C24XX_I2S_DEBUG 0 #if S3C24XX_I2S_DEBUG -#define DBG(x...) printk(KERN_DEBUG x) +#define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x) #else #define DBG(x...) #endif @@ -89,7 +89,7 @@ static void s3c24xx_snd_txctrl(int on) u32 iiscon; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -134,7 +134,7 @@ static void s3c24xx_snd_rxctrl(int on) u32 iiscon; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -159,10 +159,10 @@ static void s3c24xx_snd_rxctrl(int on) * DMA engine will simply freeze randomly. */ - iisfcon &= ~S3C2410_IISFCON_RXENABLE; - iisfcon &= ~S3C2410_IISFCON_RXDMA; - iiscon |= S3C2410_IISCON_RXIDLE; - iiscon &= ~S3C2410_IISCON_RXDMAEN; + iisfcon &= ~S3C2410_IISFCON_RXENABLE; + iisfcon &= ~S3C2410_IISFCON_RXDMA; + iiscon |= S3C2410_IISCON_RXIDLE; + iiscon &= ~S3C2410_IISCON_RXDMAEN; iismod &= ~S3C2410_IISMOD_RXMODE; writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); @@ -182,7 +182,7 @@ static int s3c24xx_snd_lrsync(void) u32 iiscon; unsigned long timeout = jiffies + msecs_to_jiffies(5); - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); while (1) { iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); @@ -201,7 +201,7 @@ static int s3c24xx_snd_lrsync(void) */ static inline int s3c24xx_snd_is_clkmaster(void) { - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1; } @@ -214,7 +214,7 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, { u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); DBG("hw_params r: IISMOD: %lx \n", iismod); @@ -250,7 +250,7 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; u32 iismod; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out; @@ -278,7 +278,7 @@ static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) { int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -320,7 +320,7 @@ static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, { u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); iismod &= ~S3C2440_IISMOD_MPLL; @@ -346,7 +346,7 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, { u32 reg; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); switch (div_id) { case S3C24XX_DIV_BCLK: @@ -381,13 +381,13 @@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); static int s3c24xx_i2s_probe(struct platform_device *pdev) { - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); if (s3c24xx_i2s.regs == NULL) return -ENXIO; - s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis"); + s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis"); if (s3c24xx_i2s.iis_clk == NULL) { DBG("failed to get iis_clock\n"); iounmap(s3c24xx_i2s.regs); @@ -411,9 +411,11 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -int s3c24xx_i2s_suspend(struct platform_device *pdev, +static int s3c24xx_i2s_suspend(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai) { + DBG("Entered %s\n", __func__); + s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); @@ -424,9 +426,10 @@ int s3c24xx_i2s_suspend(struct platform_device *pdev, return 0; } -int s3c24xx_i2s_resume(struct platform_device *pdev, +static int s3c24xx_i2s_resume(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai) { + DBG("Entered %s\n", __func__); clk_enable(s3c24xx_i2s.iis_clk); writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 29a6c82f873..49580fb481d 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -39,7 +39,7 @@ #define S3C24XX_PCM_DEBUG 0 #if S3C24XX_PCM_DEBUG -#define DBG(x...) printk(KERN_DEBUG x) +#define DBG(x...) printk(KERN_DEBUG "s3c24xx-pcm: " x) #else #define DBG(x...) #endif @@ -88,7 +88,7 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) dma_addr_t pos = prtd->dma_pos; int ret; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); while (prtd->dma_loaded < prtd->dma_limit) { unsigned long len = prtd->dma_period; @@ -98,7 +98,7 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) if ((pos + len) > prtd->dma_end) { len = prtd->dma_end - pos; DBG(KERN_DEBUG "%s: corrected dma len %ld\n", - __FUNCTION__, len); + __func__, len); } ret = s3c2410_dma_enqueue(prtd->params->channel, @@ -123,7 +123,7 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, struct snd_pcm_substream *substream = dev_id; struct s3c24xx_runtime_data *prtd; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) return; @@ -152,7 +152,7 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, unsigned long totbytes = params_buffer_bytes(params); int ret=0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ @@ -200,7 +200,7 @@ static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* TODO - do we need to ensure DMA flushed */ snd_pcm_set_runtime_buffer(substream, NULL); @@ -218,7 +218,7 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ @@ -263,7 +263,7 @@ static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); spin_lock(&prtd->lock); @@ -301,7 +301,7 @@ static snd_pcm_uframes_t unsigned long res; dma_addr_t src, dst; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); spin_lock(&prtd->lock); s3c2410_dma_getposition(prtd->params->channel, &src, &dst); @@ -334,7 +334,7 @@ static int s3c24xx_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware); @@ -353,7 +353,7 @@ static int s3c24xx_pcm_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct s3c24xx_runtime_data *prtd = runtime->private_data; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (prtd) kfree(prtd); @@ -368,7 +368,7 @@ static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); return dma_mmap_writecombine(substream->pcm->card->dev, vma, runtime->dma_area, @@ -394,7 +394,7 @@ static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) struct snd_dma_buffer *buf = &substream->dma_buffer; size_t size = s3c24xx_pcm_hardware.buffer_bytes_max; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); buf->dev.type = SNDRV_DMA_TYPE_DEV; buf->dev.dev = pcm->card->dev; @@ -413,7 +413,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm) struct snd_dma_buffer *buf; int stream; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); for (stream = 0; stream < 2; stream++) { substream = pcm->streams[stream].substream; @@ -437,7 +437,7 @@ static int s3c24xx_pcm_new(struct snd_card *card, { int ret = 0; - DBG("Entered %s\n", __FUNCTION__); + DBG("Entered %s\n", __func__); if (!card->dev->dma_mask) card->dev->dma_mask = &s3c24xx_pcm_dmamask; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index f03220d23e7..4c1e013381c 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,4 +1,5 @@ menu "SoC Audio support for SuperH" + depends on SUPERH config SND_SOC_PCM_SH7760 tristate "SoC Audio support for Renesas SH7760" diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9eb5479787c..e148db940cf 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -839,6 +839,7 @@ static int soc_remove(struct platform_device *pdev) static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", + .owner = THIS_MODULE, }, .probe = soc_probe, .remove = soc_remove, @@ -1601,3 +1602,4 @@ module_exit(snd_soc_exit); MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); MODULE_DESCRIPTION("ALSA SoC Core"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:soc-audio"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 620d7ea3c15..af3326c6350 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -226,7 +226,7 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget) snd_soc_write(codec, widget->reg, new); pop_wait(POP_TIME); } - dbg("reg old %x new %x change %d\n", old, new, change); + dbg("reg %x old %x new %x change %d\n", widget->reg, old, new, change); return change; } @@ -1288,7 +1288,7 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, mutex_unlock(&codec->mutex); dapm_power_widgets(codec, event); - dump_dapm(codec, __FUNCTION__); + dump_dapm(codec, __func__); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); @@ -1334,10 +1334,11 @@ int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, list_for_each_entry(w, &codec->dapm_widgets, list) { if (!strcmp(w->name, endpoint)) { w->connected = status; + return 0; } } - return 0; + return -ENODEV; } EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint); diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index 89d6e9c3514..09802e8a6fb 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -118,7 +118,7 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, /* Replaced by chip->bitrate later. */ .rate_max = 50000, /* Replaced by chip->bitrate later. */ - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 64 * 1024 - 1, .period_bytes_min = 512, @@ -133,7 +133,8 @@ static struct snd_pcm_hardware snd_at73c213_playback_hw = { static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) { unsigned long ssc_rate = clk_get_rate(chip->ssc->clk); - unsigned long dac_rate_new, ssc_div, status; + unsigned long dac_rate_new, ssc_div; + int status; unsigned long ssc_div_max, ssc_div_min; int max_tries; @@ -209,7 +210,13 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) { struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + int err; + /* ensure buffer_size is a multiple of period_size */ + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; snd_at73c213_playback_hw.rate_min = chip->bitrate; snd_at73c213_playback_hw.rate_max = chip->bitrate; runtime->hw = snd_at73c213_playback_hw; @@ -228,6 +235,14 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) static int snd_at73c213_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + int channels = params_channels(hw_params); + int val; + + val = ssc_readl(chip->ssc->regs, TFMR); + val = SSC_BFINS(TFMR_DATNB, channels - 1, val); + ssc_writel(chip->ssc->regs, TFMR, val); + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } @@ -249,10 +264,12 @@ static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream) ssc_writel(chip->ssc->regs, PDC_TPR, (long)runtime->dma_addr); - ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TCR, + runtime->period_size * runtime->channels); ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + block_size); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); return 0; } @@ -314,15 +331,6 @@ static struct snd_pcm_ops at73c213_playback_ops = { .pointer = snd_at73c213_pcm_pointer, }; -static void snd_at73c213_pcm_free(struct snd_pcm *pcm) -{ - struct snd_at73c213 *chip = snd_pcm_chip(pcm); - if (chip->pcm) { - snd_pcm_lib_preallocate_free_for_all(chip->pcm); - chip->pcm = NULL; - } -} - static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) { struct snd_pcm *pcm; @@ -334,7 +342,6 @@ static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) goto out; pcm->private_data = chip; - pcm->private_free = snd_at73c213_pcm_free; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; strcpy(pcm->name, "at73c213"); chip->pcm = pcm; @@ -375,7 +382,8 @@ static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) ssc_writel(chip->ssc->regs, PDC_TNPR, (long)runtime->dma_addr + offset); - ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNCR, + runtime->period_size * runtime->channels); retval = IRQ_HANDLED; } @@ -737,7 +745,7 @@ cleanup: /* * Device functions */ -static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) +static int __devinit snd_at73c213_ssc_init(struct snd_at73c213 *chip) { /* * Continuous clock output. @@ -767,7 +775,7 @@ static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) return 0; } -static int snd_at73c213_chip_init(struct snd_at73c213 *chip) +static int __devinit snd_at73c213_chip_init(struct snd_at73c213 *chip) { int retval; unsigned char dac_ctrl = 0; @@ -933,7 +941,7 @@ out: return retval; } -static int snd_at73c213_probe(struct spi_device *spi) +static int __devinit snd_at73c213_probe(struct spi_device *spi) { struct snd_card *card; struct snd_at73c213 *chip; diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c index 9cc4cd8283f..24970a5c888 100644 --- a/sound/usb/caiaq/caiaq-audio.c +++ b/sound/usb/caiaq/caiaq-audio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006,2007 Daniel Mack, Karsten Wiese + * Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese * * 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 @@ -39,7 +39,8 @@ #define BYTES_PER_SAMPLE 3 #define BYTES_PER_SAMPLE_USB 4 #define MAX_BUFFER_SIZE (128*1024) - +#define MAX_ENDPOINT_SIZE 512 + #define ENDPOINT_CAPTURE 2 #define ENDPOINT_PLAYBACK 6 @@ -77,10 +78,15 @@ static void deactivate_substream(struct snd_usb_caiaqdev *dev, struct snd_pcm_substream *sub) { + unsigned long flags; + spin_lock_irqsave(&dev->spinlock, flags); + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) dev->sub_playback[sub->number] = NULL; else dev->sub_capture[sub->number] = NULL; + + spin_unlock_irqrestore(&dev->spinlock, flags); } static int @@ -97,13 +103,13 @@ static int stream_start(struct snd_usb_caiaqdev *dev) { int i, ret; - debug("stream_start(%p)\n", dev); - spin_lock_irq(&dev->spinlock); - if (dev->streaming) { - spin_unlock_irq(&dev->spinlock); + debug("%s(%p)\n", __func__, dev); + + if (dev->streaming) return -EINVAL; - } + memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); + memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); dev->input_panic = 0; dev->output_panic = 0; dev->first_packet = 1; @@ -112,37 +118,35 @@ static int stream_start(struct snd_usb_caiaqdev *dev) for (i = 0; i < N_URBS; i++) { ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC); if (ret) { - log("unable to trigger initial read #%d! (ret = %d)\n", - i, ret); + log("unable to trigger read #%d! (ret %d)\n", i, ret); dev->streaming = 0; - spin_unlock_irq(&dev->spinlock); return -EPIPE; } } - spin_unlock_irq(&dev->spinlock); return 0; } static void stream_stop(struct snd_usb_caiaqdev *dev) { int i; - - debug("stream_stop(%p)\n", dev); + + debug("%s(%p)\n", __func__, dev); if (!dev->streaming) return; dev->streaming = 0; + for (i = 0; i < N_URBS; i++) { - usb_unlink_urb(dev->data_urbs_in[i]); - usb_unlink_urb(dev->data_urbs_out[i]); + usb_kill_urb(dev->data_urbs_in[i]); + usb_kill_urb(dev->data_urbs_out[i]); } } static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) { struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); - debug("snd_usb_caiaq_substream_open(%p)\n", substream); + debug("%s(%p)\n", __func__, substream); substream->runtime->hw = dev->pcm_info; snd_pcm_limit_hw_rates(substream->runtime); return 0; @@ -152,7 +156,7 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) { struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); - debug("snd_usb_caiaq_substream_close(%p)\n", substream); + debug("%s(%p)\n", __func__, substream); if (all_substreams_zero(dev->sub_playback) && all_substreams_zero(dev->sub_capture)) { /* when the last client has stopped streaming, @@ -160,24 +164,22 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) stream_stop(dev); dev->pcm_info.rates = dev->samplerates; } - + return 0; } static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, struct snd_pcm_hw_params *hw_params) { - debug("snd_usb_caiaq_pcm_hw_params(%p)\n", sub); + debug("%s(%p)\n", __func__, sub); return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params)); } static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) { struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); - debug("snd_usb_caiaq_pcm_hw_free(%p)\n", sub); - spin_lock_irq(&dev->spinlock); + debug("%s(%p)\n", __func__, sub); deactivate_substream(dev, sub); - spin_unlock_irq(&dev->spinlock); return snd_pcm_lib_free_pages(sub); } @@ -196,12 +198,12 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - debug("snd_usb_caiaq_pcm_prepare(%p)\n", substream); + debug("%s(%p)\n", __func__, substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; else - dev->audio_in_buf_pos[index] = 0; + dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE; if (dev->streaming) return 0; @@ -220,7 +222,10 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE) * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams; - + + if (bpp > MAX_ENDPOINT_SIZE) + bpp = MAX_ENDPOINT_SIZE; + ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate, runtime->sample_bits, bpp); if (ret) @@ -247,15 +252,11 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - spin_lock(&dev->spinlock); activate_substream(dev, sub); - spin_unlock(&dev->spinlock); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - spin_lock(&dev->spinlock); deactivate_substream(dev, sub); - spin_unlock(&dev->spinlock); break; default: return -EINVAL; @@ -328,8 +329,6 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, if (all_substreams_zero(dev->sub_capture)) return; - spin_lock(&dev->spinlock); - for (i = 0; i < iso->actual_length;) { for (stream = 0; stream < dev->n_streams; stream++, i++) { sub = dev->sub_capture[stream]; @@ -345,8 +344,6 @@ static void read_in_urb_mode0(struct snd_usb_caiaqdev *dev, } } } - - spin_unlock(&dev->spinlock); } static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, @@ -358,8 +355,6 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, struct snd_pcm_substream *sub; int stream, i; - spin_lock(&dev->spinlock); - for (i = 0; i < iso->actual_length;) { if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) { for (stream = 0; @@ -393,8 +388,6 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev, } } } - - spin_unlock(&dev->spinlock); } static void read_in_urb(struct snd_usb_caiaqdev *dev, @@ -418,8 +411,6 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev, dev->input_panic ? "(input)" : "", dev->output_panic ? "(output)" : ""); } - - check_for_elapsed_periods(dev, dev->sub_capture); } static void fill_out_urb(struct snd_usb_caiaqdev *dev, @@ -429,8 +420,6 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, unsigned char *usb_buf = urb->transfer_buffer + iso->offset; struct snd_pcm_substream *sub; int stream, i; - - spin_lock(&dev->spinlock); for (i = 0; i < iso->length;) { for (stream = 0; stream < dev->n_streams; stream++, i++) { @@ -456,9 +445,6 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev, for (stream = 0; stream < dev->n_streams; stream++, i++) usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i); } - - spin_unlock(&dev->spinlock); - check_for_elapsed_periods(dev, dev->sub_playback); } static void read_completed(struct urb *urb) @@ -472,6 +458,7 @@ static void read_completed(struct urb *urb) return; dev = info->dev; + if (!dev->streaming) return; @@ -489,8 +476,12 @@ static void read_completed(struct urb *urb) out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame; if (len > 0) { + spin_lock(&dev->spinlock); fill_out_urb(dev, out, &out->iso_frame_desc[outframe]); read_in_urb(dev, urb, &urb->iso_frame_desc[frame]); + spin_unlock(&dev->spinlock); + check_for_elapsed_periods(dev, dev->sub_playback); + check_for_elapsed_periods(dev, dev->sub_capture); send_it = 1; } @@ -696,7 +687,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev) { - debug("snd_usb_caiaq_audio_free (%p)\n", dev); + debug("%s(%p)\n", __func__, dev); stream_stop(dev); free_urbs(dev->data_urbs_in); free_urbs(dev->data_urbs_out); diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c index 7c44a2c7f96..e97d8b2ac16 100644 --- a/sound/usb/caiaq/caiaq-device.c +++ b/sound/usb/caiaq/caiaq-device.c @@ -42,7 +42,7 @@ #endif MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("caiaq USB audio, version 1.3.2"); +MODULE_DESCRIPTION("caiaq USB audio, version 1.3.6"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, RigKontrol3}," @@ -456,7 +456,7 @@ static void snd_disconnect(struct usb_interface *intf) struct snd_usb_caiaqdev *dev; struct snd_card *card = dev_get_drvdata(&intf->dev); - debug("snd_disconnect(%p)\n", intf); + debug("%s(%p)\n", __func__, intf); if (!card) return; diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index f48838a078c..410be4aff1b 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -64,9 +64,10 @@ MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */ -static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ +/* Vendor/product IDs for this card */ +static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; +static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int nrpacks = 8; /* max. number of packets per urb */ static int async_unlink = 1; static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/ @@ -687,7 +688,7 @@ static void snd_complete_urb(struct urb *urb) int err = 0; if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || - ! subs->running || /* can be stopped during retire callback */ + !subs->running || /* can be stopped during retire callback */ (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { clear_bit(ctx->index, &subs->active_mask); @@ -710,7 +711,7 @@ static void snd_complete_sync_urb(struct urb *urb) int err = 0; if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) || - ! subs->running || /* can be stopped during retire callback */ + !subs->running || /* can be stopped during retire callback */ (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { clear_bit(ctx->index + 16, &subs->active_mask); @@ -740,7 +741,7 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t s vfree(runtime->dma_area); } runtime->dma_area = vmalloc(size); - if (! runtime->dma_area) + if (!runtime->dma_area) return -ENOMEM; runtime->dma_bytes = size; return 0; @@ -772,12 +773,12 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl async = !can_sleep && async_unlink; - if (! async && in_interrupt()) + if (!async && in_interrupt()) return 0; for (i = 0; i < subs->nurbs; i++) { if (test_bit(i, &subs->active_mask)) { - if (! test_and_set_bit(i, &subs->unlink_mask)) { + if (!test_and_set_bit(i, &subs->unlink_mask)) { struct urb *u = subs->dataurb[i].urb; if (async) usb_unlink_urb(u); @@ -789,7 +790,7 @@ static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sl if (subs->syncpipe) { for (i = 0; i < SYNC_URBS; i++) { if (test_bit(i+16, &subs->active_mask)) { - if (! test_and_set_bit(i+16, &subs->unlink_mask)) { + if (!test_and_set_bit(i+16, &subs->unlink_mask)) { struct urb *u = subs->syncurb[i].urb; if (async) usb_unlink_urb(u); @@ -1137,12 +1138,12 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri if (subs->fmt_type == USB_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); - if (! u->urb) + if (!u->urb) goto out_of_memory; u->urb->transfer_buffer = usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL, &u->urb->transfer_dma); - if (! u->urb->transfer_buffer) + if (!u->urb->transfer_buffer) goto out_of_memory; u->urb->pipe = subs->datapipe; u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1155,7 +1156,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri /* allocate and initialize sync urbs */ subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4, GFP_KERNEL, &subs->sync_dma); - if (! subs->syncbuf) + if (!subs->syncbuf) goto out_of_memory; for (i = 0; i < SYNC_URBS; i++) { struct snd_urb_ctx *u = &subs->syncurb[i]; @@ -1163,7 +1164,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri u->subs = subs; u->packets = 1; u->urb = usb_alloc_urb(1, GFP_KERNEL); - if (! u->urb) + if (!u->urb) goto out_of_memory; u->urb->transfer_buffer = subs->syncbuf + i * 4; u->urb->transfer_dma = subs->sync_dma + i * 4; @@ -1427,8 +1428,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) subs->cur_audiofmt = fmt; #if 0 - printk("setting done: format = %d, rate = %d, channels = %d\n", - fmt->format, fmt->rate, fmt->channels); + printk("setting done: format = %d, rate = %d..%d, channels = %d\n", + fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels); printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n", subs->datapipe, subs->syncpipe); #endif @@ -1463,7 +1464,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, rate = params_rate(hw_params); channels = params_channels(hw_params); fmt = find_format(subs, format, rate, channels); - if (! fmt) { + if (!fmt) { snd_printd(KERN_DEBUG "cannot set format: format = 0x%x, rate = %d, channels = %d\n", format, rate, channels); return -EINVAL; @@ -1584,7 +1585,7 @@ static int hw_check_valid_format(struct snd_pcm_hw_params *params, struct audiof struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); /* check the format */ - if (! snd_mask_test(fmts, fp->format)) { + if (!snd_mask_test(fmts, fp->format)) { hwc_debug(" > check: no supported format %d\n", fp->format); return 0; } @@ -1620,7 +1621,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (! hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(params, fp)) continue; if (changed++) { if (rmin > fp->rate_min) @@ -1633,7 +1634,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, } } - if (! changed) { + if (!changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1674,7 +1675,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (! hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(params, fp)) continue; if (changed++) { if (rmin > fp->channels) @@ -1687,7 +1688,7 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, } } - if (! changed) { + if (!changed) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1727,7 +1728,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, list_for_each(p, &subs->fmt_list) { struct audioformat *fp; fp = list_entry(p, struct audioformat, list); - if (! hw_check_valid_format(params, fp)) + if (!hw_check_valid_format(params, fp)) continue; fbits |= (1ULL << fp->format); } @@ -1736,7 +1737,7 @@ static int hw_rule_format(struct snd_pcm_hw_params *params, oldbits[1] = fmt->bits[1]; fmt->bits[0] &= (u32)fbits; fmt->bits[1] &= (u32)(fbits >> 32); - if (! fmt->bits[0] && ! fmt->bits[1]) { + if (!fmt->bits[0] && !fmt->bits[1]) { hwc_debug(" --> get empty\n"); return -EINVAL; } @@ -1762,8 +1763,10 @@ static int check_hw_params_convention(struct snd_usb_substream *subs) channels = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); rates = kcalloc(MAX_MASK, sizeof(u32), GFP_KERNEL); - if (!channels || !rates) + if (!channels || !rates) { + err = -ENOMEM; goto __out; + } list_for_each(p, &subs->fmt_list) { struct audioformat *f; @@ -1916,7 +1919,10 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre 1000 * MIN_PACKS_URB, /*(nrpacks * MAX_URBS) * 1000*/ UINT_MAX); - if (check_hw_params_convention(subs)) { + err = check_hw_params_convention(subs); + if (err < 0) + return err; + else if (err) { hwc_debug("setting extra hw constraints...\n"); if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, subs, @@ -2222,7 +2228,7 @@ static void proc_pcm_format_add(struct snd_usb_stream *stream) struct snd_card *card = stream->chip->card; sprintf(name, "stream%d", stream->pcm_index); - if (! snd_card_proc_new(card, name, &entry)) + if (!snd_card_proc_new(card, name, &entry)) snd_info_set_text_ops(entry, stream, proc_pcm_format_read); } @@ -2278,7 +2284,7 @@ static void free_substream(struct snd_usb_substream *subs) { struct list_head *p, *n; - if (! subs->num_formats) + if (!subs->num_formats) return; /* not initialized */ list_for_each_safe(p, n, &subs->fmt_list) { struct audioformat *fp = list_entry(p, struct audioformat, list); @@ -2328,7 +2334,7 @@ static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct aud if (as->fmt_type != fp->fmt_type) continue; subs = &as->substream[stream]; - if (! subs->endpoint) + if (!subs->endpoint) continue; if (subs->endpoint == fp->endpoint) { list_add_tail(&fp->list, &subs->fmt_list); @@ -2354,7 +2360,7 @@ static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct aud /* create a new pcm */ as = kzalloc(sizeof(*as), GFP_KERNEL); - if (! as) + if (!as) return -ENOMEM; as->pcm_index = chip->pcm_devs; as->chip = chip; @@ -2463,11 +2469,12 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip, struct audiofor } break; case USB_AUDIO_FORMAT_PCM8: - /* Dallas DS4201 workaround */ + pcm_format = SNDRV_PCM_FORMAT_U8; + + /* Dallas DS4201 workaround: it advertises U8 format, but really + supports S8. */ if (chip->usb_id == USB_ID(0x04fa, 0x4201)) pcm_format = SNDRV_PCM_FORMAT_S8; - else - pcm_format = SNDRV_PCM_FORMAT_U8; break; case USB_AUDIO_FORMAT_IEEE_FLOAT: pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; @@ -2671,12 +2678,23 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no) int format; struct audioformat *fp; unsigned char *fmt, *csep; + int num; dev = chip->dev; /* parse the interface's altsettings */ iface = usb_ifnum_to_if(dev, iface_no); - for (i = 0; i < iface->num_altsetting; i++) { + + num = iface->num_altsetting; + + /* + * Dallas DS4201 workaround: It presents 5 altsettings, but the last + * one misses syncpipe, and does not produce any sound. + */ + if (chip->usb_id == USB_ID(0x04fa, 0x4201)) + num = 4; + + for (i = 0; i < num; i++) { alts = &iface->altsetting[i]; altsd = get_iface_desc(alts); /* skip invalid one */ @@ -3375,14 +3393,14 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip, static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_usb_audio *chip = entry->private_data; - if (! chip->shutdown) + if (!chip->shutdown) snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum); } static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_usb_audio *chip = entry->private_data; - if (! chip->shutdown) + if (!chip->shutdown) snd_iprintf(buffer, "%04x:%04x\n", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); @@ -3391,9 +3409,9 @@ static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_ static void snd_usb_audio_create_proc(struct snd_usb_audio *chip) { struct snd_info_entry *entry; - if (! snd_card_proc_new(chip->card, "usbbus", &entry)) + if (!snd_card_proc_new(chip->card, "usbbus", &entry)) snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read); - if (! snd_card_proc_new(chip->card, "usbid", &entry)) + if (!snd_card_proc_new(chip->card, "usbid", &entry)) snd_info_set_text_ops(entry, chip, proc_audio_usbid_read); } @@ -3406,7 +3424,6 @@ static void snd_usb_audio_create_proc(struct snd_usb_audio *chip) static int snd_usb_audio_free(struct snd_usb_audio *chip) { - usb_chip[chip->index] = NULL; kfree(chip); return 0; } @@ -3600,8 +3617,8 @@ static void *snd_usb_audio_probe(struct usb_device *dev, snd_card_set_dev(chip->card, &intf->dev); break; } - if (! chip) { - snd_printk(KERN_ERR "no available usb audio device\n"); + if (!chip) { + printk(KERN_ERR "no available usb audio device\n"); goto __error; } } @@ -3671,6 +3688,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) list_for_each(p, &chip->mixer_list) { snd_usb_mixer_disconnect(p); } + usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); snd_card_free_when_closed(card); } else { diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 938dff5f9ce..82a8d14c26a 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -39,6 +39,30 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC +/* Creative/E-Mu devices */ +{ + USB_DEVICE(0x041e, 0x3010), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Creative Labs", + .product_name = "Sound Blaster MP3+", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + /* E-Mu 0202 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f02, + .bInterfaceClass = USB_CLASS_AUDIO, +}, +{ + /* E-Mu 0404 USB */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x041e, + .idProduct = 0x3f04, + .bInterfaceClass = USB_CLASS_AUDIO, +}, + /* * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface * class matches do not take effect without an explicit ID match. @@ -97,19 +121,7 @@ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL }, -/* E-Mu devices */ -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f02, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f04, - .bInterfaceClass = USB_CLASS_AUDIO, -}, + /* * Yamaha devices */ @@ -1165,19 +1177,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, -{ - USB_DEVICE(0x582, 0x00a6), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "Juno-G", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, { /* * This quirk is for the "Advanced" modes of the Edirol UA-25. * If the switch is not in an advanced setting, the UA-25 has @@ -1336,6 +1335,19 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* TODO: add Edirol MD-P1 support */ { + USB_DEVICE(0x582, 0x00a6), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "Juno-G", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ /* Roland SH-201 */ USB_DEVICE(0x0582, 0x00ad), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -1719,17 +1731,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -{ - /* Creative Sound Blaster MP3+ */ - USB_DEVICE(0x041e, 0x3010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Creative Labs", - .product_name = "Sound Blaster MP3+", - .ifnum = QUIRK_NO_INTERFACE - } - -}, - /* Emagic devices */ { USB_DEVICE(0x086a, 0x0001), |