From 8f34692f63d66805b51ff408f4067748d3c1c3fd Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:36 +0200 Subject: ALSA: ak4620 support, codec regs listed in proc * complete support for ak4620 * codec regs listed in proc for all codecs/chips * adding total regs for each codec * fixing nb. of steps in input attenuation controls Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai --- sound/pci/ice1712/juli.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index fd948bfd9aef..4789e8bfdc17 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -412,25 +412,6 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = { }, }; - -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, @@ -496,8 +477,6 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) /* only capture SPDIF over AK4114 */ err = snd_ak4114_build(spec->ak4114, NULL, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - - ak4358_proc_init(ice); if (err < 0) return err; return 0; -- cgit v1.2.2 From 494703062b6e6ef5e72364aafc9bcbc172d53dea Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:38 +0200 Subject: ALSA: ice1724 - adding GPIO routines for mask and direction * get/set routines for GPIO mask and direction Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1712.c | 12 ++++++++++++ sound/pci/ice1712/ice1712.h | 7 +++++++ sound/pci/ice1712/ice1724.c | 19 +++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index cecf1ffeeaaa..56d8d67f1ac3 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -298,6 +298,16 @@ static void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data) inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */ } +static unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice) +{ + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION); +} + +static unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice) +{ + return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK); +} + static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) { snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data); @@ -2557,7 +2567,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card, mutex_init(&ice->i2c_mutex); mutex_init(&ice->open_mutex); ice->gpio.set_mask = snd_ice1712_set_gpio_mask; + ice->gpio.get_mask = snd_ice1712_get_gpio_mask; ice->gpio.set_dir = snd_ice1712_set_gpio_dir; + ice->gpio.get_dir = snd_ice1712_get_gpio_dir; ice->gpio.set_data = snd_ice1712_set_gpio_data; ice->gpio.get_data = snd_ice1712_get_gpio_data; diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 9da2dae64c5b..b31a59d0625c 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -359,7 +359,9 @@ struct snd_ice1712 { unsigned int saved[2]; /* for ewx_i2c */ /* operators */ void (*set_mask)(struct snd_ice1712 *ice, unsigned int data); + unsigned int (*get_mask)(struct snd_ice1712 *ice); void (*set_dir)(struct snd_ice1712 *ice, unsigned int data); + unsigned int (*get_dir)(struct snd_ice1712 *ice); void (*set_data)(struct snd_ice1712 *ice, unsigned int data); unsigned int (*get_data)(struct snd_ice1712 *ice); /* misc operators - move to another place? */ @@ -399,6 +401,11 @@ static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned in ice->gpio.set_dir(ice, bits); } +static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice) +{ + return ice->gpio.get_dir(ice); +} + static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits) { ice->gpio.set_mask(ice, bits); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index af6e00148621..2213beec009a 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -196,6 +196,12 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data) inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */ } +/* get gpio direction 0 = read, 1 = write */ +static unsigned int snd_vt1724_get_gpio_dir(struct snd_ice1712 *ice) +{ + return inl(ICEREG1724(ice, GPIO_DIRECTION)); +} + /* set the gpio mask (0 = writable) */ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) { @@ -205,6 +211,17 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data) inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */ } +static unsigned int snd_vt1724_get_gpio_mask(struct snd_ice1712 *ice) +{ + unsigned int mask; + if (!ice->vt1720) + mask = (unsigned int)inb(ICEREG1724(ice, GPIO_WRITE_MASK_22)); + else + mask = 0; + mask = (mask << 16) | inw(ICEREG1724(ice, GPIO_WRITE_MASK)); + return mask; +} + static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data) { outw(data, ICEREG1724(ice, GPIO_DATA)); @@ -2434,7 +2451,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card, mutex_init(&ice->open_mutex); mutex_init(&ice->i2c_mutex); ice->gpio.set_mask = snd_vt1724_set_gpio_mask; + ice->gpio.get_mask = snd_vt1724_get_gpio_mask; ice->gpio.set_dir = snd_vt1724_set_gpio_dir; + ice->gpio.get_dir = snd_vt1724_get_gpio_dir; ice->gpio.set_data = snd_vt1724_set_gpio_data; ice->gpio.get_data = snd_vt1724_get_gpio_data; ice->card = card; -- cgit v1.2.2 From 6796d5a05f4d3caad17d2586b3e5776fda50ef82 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:39 +0200 Subject: ALSA: ice1724 - pro-rate-locking makes sense only for internal clock mode * pro-rate-locking applies to internal clock mode only * required rate and current rate are compared for internal clock mode only Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1724.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 2213beec009a..514e15385f7a 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -118,9 +118,12 @@ static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice) return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0; } +/* + * locking rate makes sense only for internal clock mode + */ static inline int is_pro_rate_locked(struct snd_ice1712 *ice) { - return ice->is_spdif_master(ice) || PRO_RATE_LOCKED; + return (!ice->is_spdif_master(ice)) && PRO_RATE_LOCKED; } /* @@ -668,16 +671,22 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, return -EBUSY; } if (!force && is_pro_rate_locked(ice)) { + /* comparing required and current rate - makes sense for + * internal clock only */ spin_unlock_irqrestore(&ice->reg_lock, flags); return (rate == ice->cur_rate) ? 0 : -EBUSY; } - 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 0; + if (force || !ice->is_spdif_master(ice)) { + /* force means the rate was switched by ucontrol, otherwise + * setting clock rate for internal clock mode */ + 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 0; + } } ice->cur_rate = rate; -- cgit v1.2.2 From 1ff97cb9dd9f53b33ce6710a4f861f43e70e8ca4 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:40 +0200 Subject: ALSA: ice1724 - Support for multiple external clock types * Support for customization of the external clock names * Adding hooks to playback_pro_open and capture_pro_open, allowing e.g. limiting available stream rates to a single value when the external clock rate is detected Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1712.h | 7 ++++-- sound/pci/ice1712/ice1724.c | 58 +++++++++++++++++++++++++++++++++++---------- sound/pci/ice1712/juli.c | 3 ++- 3 files changed, 52 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index b31a59d0625c..4615bca39e18 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -379,8 +379,11 @@ struct snd_ice1712 { 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); - + int (*set_spdif_clock)(struct snd_ice1712 *ice, int type); + int (*get_spdif_master_type)(struct snd_ice1712 *ice); + char **ext_clock_names; + int ext_clock_count; + void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *); #ifdef CONFIG_PM int (*pm_suspend)(struct snd_ice1712 *); int (*pm_resume)(struct snd_ice1712 *); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 514e15385f7a..3f11195b2631 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -104,6 +104,8 @@ static int PRO_RATE_LOCKED; static int PRO_RATE_RESET = 1; static unsigned int PRO_RATE_DEFAULT = 44100; +static char *ext_clock_names[1] = { "IEC958 In" }; + /* * Basic I/O */ @@ -1042,6 +1044,8 @@ static int snd_vt1724_playback_pro_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->pro_open) + ice->pro_open(ice, substream); return 0; } @@ -1060,6 +1064,8 @@ static int snd_vt1724_capture_pro_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->pro_open) + ice->pro_open(ice, substream); return 0; } @@ -1813,15 +1819,21 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - + int hw_rates_count = ice->hw_rates->count; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = ice->hw_rates->count + 1; + + uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count; + /* upper limit - keep at top */ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1) - strcpy(uinfo->value.enumerated.name, "IEC958 Input"); + if (uinfo->value.enumerated.item >= hw_rates_count) + /* ext_clock items */ + strcpy(uinfo->value.enumerated.name, + ice->ext_clock_names[ + uinfo->value.enumerated.item - hw_rates_count]); else + /* int clock items */ sprintf(uinfo->value.enumerated.name, "%d", ice->hw_rates->list[uinfo->value.enumerated.item]); return 0; @@ -1835,7 +1847,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, spin_lock_irq(&ice->reg_lock); if (ice->is_spdif_master(ice)) { - ucontrol->value.enumerated.item[0] = ice->hw_rates->count; + ucontrol->value.enumerated.item[0] = ice->hw_rates->count + + ice->get_spdif_master_type(ice); } else { rate = ice->get_rate(ice); ucontrol->value.enumerated.item[0] = 0; @@ -1850,8 +1863,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol, return 0; } +static int stdclock_get_spdif_master_type(struct snd_ice1712 *ice) +{ + /* standard external clock - only single type - SPDIF IN */ + return 0; +} + /* setting clock to external - SPDIF */ -static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) +static int stdclock_set_spdif_clock(struct snd_ice1712 *ice, int type) { unsigned char oval; unsigned char i2s_oval; @@ -1860,27 +1879,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice) /* setting 256fs */ i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT)); outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT)); + return 0; } + 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 int old_rate, new_rate; unsigned int item = ucontrol->value.enumerated.item[0]; - unsigned int spdif = ice->hw_rates->count; + unsigned int first_ext_clock = ice->hw_rates->count; - if (item > spdif) + if (item > first_ext_clock + ice->ext_clock_count - 1) return -EINVAL; + /* if rate = 0 => external clock */ spin_lock_irq(&ice->reg_lock); 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); + if (item >= first_ext_clock) { + /* switching to external clock */ + ice->set_spdif_clock(ice, item - first_ext_clock); new_rate = 0; } else { /* internal on-card clock */ @@ -1892,7 +1914,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol, } spin_unlock_irq(&ice->reg_lock); - /* the first reset to the SPDIF master mode? */ + /* the first switch to the ext. clock mode? */ if (old_rate != new_rate && !new_rate) { /* notify akm chips as well */ unsigned int i; @@ -2550,6 +2572,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci, return err; } + /* field init before calling chip_init */ + ice->ext_clock_count = 0; + for (tbl = card_tables; *tbl; tbl++) { for (c = *tbl; c->subvendor; c++) { if (c->subvendor == ice->eeprom.subvendor) { @@ -2588,6 +2613,13 @@ __found: ice->set_mclk = stdclock_set_mclk; if (!ice->set_spdif_clock) ice->set_spdif_clock = stdclock_set_spdif_clock; + if (!ice->get_spdif_master_type) + ice->get_spdif_master_type = stdclock_get_spdif_master_type; + if (!ice->ext_clock_names) + ice->ext_clock_names = ext_clock_names; + if (!ice->ext_clock_count) + ice->ext_clock_count = ARRAY_SIZE(ext_clock_names); + if (!ice->hw_rates) set_std_hw_rates(ice); @@ -2747,7 +2779,7 @@ static int snd_vt1724_resume(struct pci_dev *pci) if (ice->pm_saved_is_spdif_master) { /* switching to external clock via SPDIF */ - ice->set_spdif_clock(ice); + ice->set_spdif_clock(ice, 0); } else { /* internal on-card clock */ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1); diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 4789e8bfdc17..4bed9633a4cd 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -529,13 +529,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice, } /* setting clock to external - SPDIF */ -static void juli_set_spdif_clock(struct snd_ice1712 *ice) +static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type) { 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); + return 0; } /* Called when ak4114 detects change in the input SPDIF stream */ -- cgit v1.2.2 From 6ef80706184be792499a4485a7957f2660b6a076 Mon Sep 17 00:00:00 2001 From: Pavel Hofman Date: Wed, 16 Sep 2009 22:25:41 +0200 Subject: ALSA: ice1724 - Infrasonic Quartet support * three external clock types * all controls supported Signed-off-by: Pavel Hofman Signed-off-by: Takashi Iwai --- sound/pci/ice1712/Makefile | 2 +- sound/pci/ice1712/ice1724.c | 3 + sound/pci/ice1712/quartet.c | 1130 +++++++++++++++++++++++++++++++++++++++++++ sound/pci/ice1712/quartet.h | 10 + 4 files changed, 1144 insertions(+), 1 deletion(-) create mode 100644 sound/pci/ice1712/quartet.c create mode 100644 sound/pci/ice1712/quartet.h (limited to 'sound/pci') diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 536eae2ccf94..f7ce33f00ea5 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 3f11195b2631..3896fb931de1 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -53,6 +53,7 @@ #include "phase.h" #include "wtm.h" #include "se.h" +#include "quartet.h" MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); @@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{" PHASE_DEVICE_DESC WTM_DEVICE_DESC SE_DEVICE_DESC + QTET_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -2184,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_phase_cards, snd_vt1724_wtm_cards, snd_vt1724_se_cards, + snd_vt1724_qtet_cards, NULL, }; diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c new file mode 100644 index 000000000000..1948632787e6 --- /dev/null +++ b/sound/pci/ice1712/quartet.c @@ -0,0 +1,1130 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Infrasonic Quartet + * + * Copyright (c) 2009 Pavel Hofman + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "envy24ht.h" +#include +#include "quartet.h" + +struct qtet_spec { + struct ak4113 *ak4113; + unsigned int scr; /* system control register */ + unsigned int mcr; /* monitoring control register */ + unsigned int cpld; /* cpld register */ +}; + +struct qtet_kcontrol_private { + unsigned int bit; + void (*set_register)(struct snd_ice1712 *ice, unsigned int val); + unsigned int (*get_register)(struct snd_ice1712 *ice); + unsigned char *texts[2]; +}; + +enum { + IN12_SEL = 0, + IN34_SEL, + AIN34_SEL, + COAX_OUT, + IN12_MON12, + IN12_MON34, + IN34_MON12, + IN34_MON34, + OUT12_MON34, + OUT34_MON12, +}; + +static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS", + "Word Clock 256xFS"}; + +/* chip address on I2C bus */ +#define AK4113_ADDR 0x26 /* S/PDIF receiver */ + +/* chip address on SPI bus */ +#define AK4620_ADDR 0x02 /* ADC/DAC */ + + +/* + * GPIO pins + */ + +/* GPIO0 - O - DATA0, def. 0 */ +#define GPIO_D0 (1<<0) +/* GPIO1 - I/O - DATA1, Jack Detect Input0 (0:present, 1:missing), def. 1 */ +#define GPIO_D1_JACKDTC0 (1<<1) +/* GPIO2 - I/O - DATA2, Jack Detect Input1 (0:present, 1:missing), def. 1 */ +#define GPIO_D2_JACKDTC1 (1<<2) +/* GPIO3 - I/O - DATA3, def. 1 */ +#define GPIO_D3 (1<<3) +/* GPIO4 - I/O - DATA4, SPI CDTO, def. 1 */ +#define GPIO_D4_SPI_CDTO (1<<4) +/* GPIO5 - I/O - DATA5, SPI CCLK, def. 1 */ +#define GPIO_D5_SPI_CCLK (1<<5) +/* GPIO6 - I/O - DATA6, Cable Detect Input (0:detected, 1:not detected */ +#define GPIO_D6_CD (1<<6) +/* GPIO7 - I/O - DATA7, Device Detect Input (0:detected, 1:not detected */ +#define GPIO_D7_DD (1<<7) +/* GPIO8 - O - CPLD Chip Select, def. 1 */ +#define GPIO_CPLD_CSN (1<<8) +/* GPIO9 - O - CPLD register read/write (0:write, 1:read), def. 0 */ +#define GPIO_CPLD_RW (1<<9) +/* GPIO10 - O - SPI Chip Select for CODEC#0, def. 1 */ +#define GPIO_SPI_CSN0 (1<<10) +/* GPIO11 - O - SPI Chip Select for CODEC#1, def. 1 */ +#define GPIO_SPI_CSN1 (1<<11) +/* GPIO12 - O - Ex. Register Output Enable (0:enable, 1:disable), def. 1, + * init 0 */ +#define GPIO_EX_GPIOE (1<<12) +/* GPIO13 - O - Ex. Register0 Chip Select for System Control Register, + * def. 1 */ +#define GPIO_SCR (1<<13) +/* GPIO14 - O - Ex. Register1 Chip Select for Monitor Control Register, + * def. 1 */ +#define GPIO_MCR (1<<14) + +#define GPIO_SPI_ALL (GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK |\ + GPIO_SPI_CSN0 | GPIO_SPI_CSN1) + +#define GPIO_DATA_MASK (GPIO_D0 | GPIO_D1_JACKDTC0 | \ + GPIO_D2_JACKDTC1 | GPIO_D3 | \ + GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK | \ + GPIO_D6_CD | GPIO_D7_DD) + +/* System Control Register GPIO_SCR data bits */ +/* Mic/Line select relay (0:line, 1:mic) */ +#define SCR_RELAY GPIO_D0 +/* Phantom power drive control (0:5V, 1:48V) */ +#define SCR_PHP_V GPIO_D1_JACKDTC0 +/* H/W mute control (0:Normal, 1:Mute) */ +#define SCR_MUTE GPIO_D2_JACKDTC1 +/* Phantom power control (0:Phantom on, 1:off) */ +#define SCR_PHP GPIO_D3 +/* Analog input 1/2 Source Select */ +#define SCR_AIN12_SEL0 GPIO_D4_SPI_CDTO +#define SCR_AIN12_SEL1 GPIO_D5_SPI_CCLK +/* Analog input 3/4 Source Select (0:line, 1:hi-z) */ +#define SCR_AIN34_SEL GPIO_D6_CD +/* Codec Power Down (0:power down, 1:normal) */ +#define SCR_CODEC_PDN GPIO_D7_DD + +#define SCR_AIN12_LINE (0) +#define SCR_AIN12_MIC (SCR_AIN12_SEL0) +#define SCR_AIN12_LOWCUT (SCR_AIN12_SEL1 | SCR_AIN12_SEL0) + +/* Monitor Control Register GPIO_MCR data bits */ +/* Input 1/2 to Monitor 1/2 (0:off, 1:on) */ +#define MCR_IN12_MON12 GPIO_D0 +/* Input 1/2 to Monitor 3/4 (0:off, 1:on) */ +#define MCR_IN12_MON34 GPIO_D1_JACKDTC0 +/* Input 3/4 to Monitor 1/2 (0:off, 1:on) */ +#define MCR_IN34_MON12 GPIO_D2_JACKDTC1 +/* Input 3/4 to Monitor 3/4 (0:off, 1:on) */ +#define MCR_IN34_MON34 GPIO_D3 +/* Output to Monitor 1/2 (0:off, 1:on) */ +#define MCR_OUT34_MON12 GPIO_D4_SPI_CDTO +/* Output to Monitor 3/4 (0:off, 1:on) */ +#define MCR_OUT12_MON34 GPIO_D5_SPI_CCLK + +/* CPLD Register DATA bits */ +/* Clock Rate Select */ +#define CPLD_CKS0 GPIO_D0 +#define CPLD_CKS1 GPIO_D1_JACKDTC0 +#define CPLD_CKS2 GPIO_D2_JACKDTC1 +/* Sync Source Select (0:Internal, 1:External) */ +#define CPLD_SYNC_SEL GPIO_D3 +/* Word Clock FS Select (0:FS, 1:256FS) */ +#define CPLD_WORD_SEL GPIO_D4_SPI_CDTO +/* Coaxial Output Source (IS-Link) (0:SPDIF, 1:I2S) */ +#define CPLD_COAX_OUT GPIO_D5_SPI_CCLK +/* Input 1/2 Source Select (0:Analog12, 1:An34) */ +#define CPLD_IN12_SEL GPIO_D6_CD +/* Input 3/4 Source Select (0:Analog34, 1:Digital In) */ +#define CPLD_IN34_SEL GPIO_D7_DD + +/* internal clock (CPLD_SYNC_SEL = 0) options */ +#define CPLD_CKS_44100HZ (0) +#define CPLD_CKS_48000HZ (CPLD_CKS0) +#define CPLD_CKS_88200HZ (CPLD_CKS1) +#define CPLD_CKS_96000HZ (CPLD_CKS1 | CPLD_CKS0) +#define CPLD_CKS_176400HZ (CPLD_CKS2) +#define CPLD_CKS_192000HZ (CPLD_CKS2 | CPLD_CKS0) + +#define CPLD_CKS_MASK (CPLD_CKS0 | CPLD_CKS1 | CPLD_CKS2) + +/* external clock (CPLD_SYNC_SEL = 1) options */ +/* external clock - SPDIF */ +#define CPLD_EXT_SPDIF (0 | CPLD_SYNC_SEL) +/* external clock - WordClock 1xfs */ +#define CPLD_EXT_WORDCLOCK_1FS (CPLD_CKS1 | CPLD_SYNC_SEL) +/* external clock - WordClock 256xfs */ +#define CPLD_EXT_WORDCLOCK_256FS (CPLD_CKS1 | CPLD_WORD_SEL |\ + CPLD_SYNC_SEL) + +#define EXT_SPDIF_TYPE 0 +#define EXT_WORDCLOCK_1FS_TYPE 1 +#define EXT_WORDCLOCK_256FS_TYPE 2 + +#define AK4620_DFS0 (1<<0) +#define AK4620_DFS1 (1<<1) +#define AK4620_CKS0 (1<<2) +#define AK4620_CKS1 (1<<3) +/* Clock and Format Control register */ +#define AK4620_DFS_REG 0x02 + +/* Deem and Volume Control register */ +#define AK4620_DEEMVOL_REG 0x03 +#define AK4620_SMUTE (1<<7) + +/* + * Conversion from int value to its binary form. Used for debugging. + * The output buffer must be allocated prior to calling the function. + */ +static char *get_binary(char *buffer, int value) +{ + int i, j, pos; + pos = 0; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 8; ++j) { + if (value & (1 << (31-(i*8 + j)))) + buffer[pos] = '1'; + else + buffer[pos] = '0'; + pos++; + } + if (i < 3) { + buffer[pos] = ' '; + pos++; + } + } + buffer[pos] = '\0'; + return buffer; +} + +/* + * Initial setup of the conversion array GPIO <-> rate + */ +static unsigned int qtet_rates[] = { + 44100, 48000, 88200, + 96000, 176400, 192000, +}; + +static unsigned int cks_vals[] = { + CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ, + CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ, +}; + +static struct snd_pcm_hw_constraint_list qtet_rates_info = { + .count = ARRAY_SIZE(qtet_rates), + .list = qtet_rates, + .mask = 0, +}; + +static void qtet_ak4113_write(void *private_data, unsigned char reg, + unsigned char val) +{ + snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4113_ADDR, + reg, val); +} + +static unsigned char qtet_ak4113_read(void *private_data, unsigned char reg) +{ + return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, + AK4113_ADDR, reg); +} + + +/* + * AK4620 section + */ + +/* + * Write data to addr register of ak4620 + */ +static void qtet_akm_write(struct snd_akm4xxx *ak, int chip, + unsigned char addr, unsigned char data) +{ + unsigned int tmp, orig_dir; + int idx; + unsigned int addrdata; + struct snd_ice1712 *ice = ak->private_data[0]; + + if (snd_BUG_ON(chip < 0 || chip >= 4)) + return; + /*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x, + data=0x%x\n", chip, addr, data);*/ + orig_dir = ice->gpio.get_dir(ice); + ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL); + /* set mask - only SPI bits */ + ice->gpio.set_mask(ice, ~GPIO_SPI_ALL); + + tmp = ice->gpio.get_data(ice); + /* high all */ + tmp |= GPIO_SPI_ALL; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop chip select */ + if (chip) + /* CODEC 1 */ + tmp &= ~GPIO_SPI_CSN1; + else + tmp &= ~GPIO_SPI_CSN0; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* build I2C address + data byte */ + addrdata = (AK4620_ADDR << 6) | 0x20 | (addr & 0x1f); + addrdata = (addrdata << 8) | data; + for (idx = 15; idx >= 0; idx--) { + /* drop clock */ + tmp &= ~GPIO_D5_SPI_CCLK; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* set data */ + if (addrdata & (1 << idx)) + tmp |= GPIO_D4_SPI_CDTO; + else + tmp &= ~GPIO_D4_SPI_CDTO; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* raise clock */ + tmp |= GPIO_D5_SPI_CCLK; + ice->gpio.set_data(ice, tmp); + udelay(100); + } + /* all back to 1 */ + tmp |= GPIO_SPI_ALL; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* return all gpios to non-writable */ + ice->gpio.set_mask(ice, 0xffffff); + /* restore GPIOs direction */ + ice->gpio.set_dir(ice, orig_dir); +} + +static void qtet_akm_set_regs(struct snd_akm4xxx *ak, unsigned char addr, + unsigned char mask, unsigned char value) +{ + unsigned char tmp; + int chip; + for (chip = 0; chip < ak->num_chips; chip++) { + tmp = snd_akm4xxx_get(ak, chip, addr); + /* clear the bits */ + tmp &= ~mask; + /* set the new bits */ + tmp |= value; + snd_akm4xxx_write(ak, chip, addr, tmp); + } +} + +/* + * change the rate of AK4620 + */ +static void qtet_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +{ + unsigned char ak4620_dfs; + + if (rate == 0) /* no hint - S/PDIF input is master or the new spdif + input rate undetected, simply return */ + return; + + /* adjust DFS on codecs - see datasheet */ + if (rate > 108000) + ak4620_dfs = AK4620_DFS1 | AK4620_CKS1; + else if (rate > 54000) + ak4620_dfs = AK4620_DFS0 | AK4620_CKS0; + else + ak4620_dfs = 0; + + /* set new value */ + qtet_akm_set_regs(ak, AK4620_DFS_REG, AK4620_DFS0 | AK4620_DFS1 | + AK4620_CKS0 | AK4620_CKS1, ak4620_dfs); +} + +#define AK_CONTROL(xname, xch) { .name = xname, .num_channels = xch } + +#define PCM_12_PLAYBACK_VOLUME "PCM 1/2 Playback Volume" +#define PCM_34_PLAYBACK_VOLUME "PCM 3/4 Playback Volume" +#define PCM_12_CAPTURE_VOLUME "PCM 1/2 Capture Volume" +#define PCM_34_CAPTURE_VOLUME "PCM 3/4 Capture Volume" + +static const struct snd_akm4xxx_dac_channel qtet_dac[] = { + AK_CONTROL(PCM_12_PLAYBACK_VOLUME, 2), + AK_CONTROL(PCM_34_PLAYBACK_VOLUME, 2), +}; + +static const struct snd_akm4xxx_adc_channel qtet_adc[] = { + AK_CONTROL(PCM_12_CAPTURE_VOLUME, 2), + AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2), +}; + +static struct snd_akm4xxx akm_qtet_dac __devinitdata = { + .type = SND_AK4620, + .num_dacs = 4, /* DAC1 - Output 12 + */ + .num_adcs = 4, /* ADC1 - Input 12 + */ + .ops = { + .write = qtet_akm_write, + .set_rate_val = qtet_akm_set_rate_val, + }, + .dac_info = qtet_dac, + .adc_info = qtet_adc, +}; + +/* Communication routines with the CPLD */ + + +/* Writes data to external register reg, both reg and data are + * GPIO representations */ +static void reg_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int tmp; + + mutex_lock(&ice->gpio_mutex); + /* set direction of used GPIOs*/ + /* all outputs */ + tmp = 0x00ffff; + ice->gpio.set_dir(ice, tmp); + /* mask - writable bits */ + ice->gpio.set_mask(ice, ~(tmp)); + /* write the data */ + tmp = ice->gpio.get_data(ice); + tmp &= ~GPIO_DATA_MASK; + tmp |= data; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop output enable */ + tmp &= ~GPIO_EX_GPIOE; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* drop the register gpio */ + tmp &= ~reg; + ice->gpio.set_data(ice, tmp); + udelay(100); + /* raise the register GPIO */ + tmp |= reg; + ice->gpio.set_data(ice, tmp); + udelay(100); + + /* raise all data gpios */ + tmp |= GPIO_DATA_MASK; + ice->gpio.set_data(ice, tmp); + /* mask - immutable bits */ + ice->gpio.set_mask(ice, 0xffffff); + /* outputs only 8-15 */ + ice->gpio.set_dir(ice, 0x00ff00); + mutex_unlock(&ice->gpio_mutex); +} + +static unsigned int get_scr(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->scr; +} + +static unsigned int get_mcr(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->mcr; +} + +static unsigned int get_cpld(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + return spec->cpld; +} + +static void set_scr(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_SCR, val); + spec->scr = val; +} + +static void set_mcr(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_MCR, val); + spec->mcr = val; +} + +static void set_cpld(struct snd_ice1712 *ice, unsigned int val) +{ + struct qtet_spec *spec = ice->spec; + reg_write(ice, GPIO_CPLD_CSN, val); + spec->cpld = val; +} +#ifdef CONFIG_PROC_FS +static void proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + char bin_buffer[36]; + + snd_iprintf(buffer, "SCR: %s\n", get_binary(bin_buffer, + get_scr(ice))); + snd_iprintf(buffer, "MCR: %s\n", get_binary(bin_buffer, + get_mcr(ice))); + snd_iprintf(buffer, "CPLD: %s\n", get_binary(bin_buffer, + get_cpld(ice))); +} + +static void proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "quartet", &entry)) + snd_info_set_text_ops(entry, ice, proc_regs_read); +} +#else /* !CONFIG_PROC_FS */ +static void proc_init(struct snd_ice1712 *ice) {} +#endif + +static int qtet_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + val = get_scr(ice) & SCR_MUTE; + ucontrol->value.integer.value[0] = (val) ? 0 : 1; + return 0; +} + +static int qtet_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new, smute; + old = get_scr(ice) & SCR_MUTE; + if (ucontrol->value.integer.value[0]) { + /* unmute */ + new = 0; + /* un-smuting DAC */ + smute = 0; + } else { + /* mute */ + new = SCR_MUTE; + /* smuting DAC */ + smute = AK4620_SMUTE; + } + if (old != new) { + struct snd_akm4xxx *ak = ice->akm; + set_scr(ice, (get_scr(ice) & ~SCR_MUTE) | new); + /* set smute */ + qtet_akm_set_regs(ak, AK4620_DEEMVOL_REG, AK4620_SMUTE, smute); + return 1; + } + /* no change */ + return 0; +} + +static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"}; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(texts); + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val, result; + val = get_scr(ice) & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + switch (val) { + case SCR_AIN12_LINE: + result = 0; + break; + case SCR_AIN12_MIC: + result = 1; + break; + case SCR_AIN12_LOWCUT: + result = 2; + break; + default: + /* BUG - no other combinations allowed */ + snd_BUG(); + result = 0; + } + ucontrol->value.integer.value[0] = result; + return 0; +} + +static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new, tmp, masked_old; + old = new = get_scr(ice); + masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + tmp = ucontrol->value.integer.value[0]; + if (tmp == 2) + tmp = 3; /* binary 10 is not supported */ + tmp <<= 4; /* shifting to SCR_AIN12_SEL0 */ + if (tmp != masked_old) { + /* change requested */ + switch (tmp) { + case SCR_AIN12_LINE: + new = old & ~(SCR_AIN12_SEL1 | SCR_AIN12_SEL0); + set_scr(ice, new); + /* turn off relay */ + new &= ~SCR_RELAY; + set_scr(ice, new); + break; + case SCR_AIN12_MIC: + /* turn on relay */ + new = old | SCR_RELAY; + set_scr(ice, new); + new = (new & ~SCR_AIN12_SEL1) | SCR_AIN12_SEL0; + set_scr(ice, new); + break; + case SCR_AIN12_LOWCUT: + /* turn on relay */ + new = old | SCR_RELAY; + set_scr(ice, new); + new |= SCR_AIN12_SEL1 | SCR_AIN12_SEL0; + set_scr(ice, new); + break; + default: + snd_BUG(); + } + return 1; + } + /* no change */ + return 0; +} + +static int qtet_php_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + /* if phantom voltage =48V, phantom on */ + val = get_scr(ice) & SCR_PHP_V; + ucontrol->value.integer.value[0] = val ? 1 : 0; + return 0; +} + +static int qtet_php_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new; + old = new = get_scr(ice); + if (ucontrol->value.integer.value[0] /* phantom on requested */ + && (~old & SCR_PHP_V)) /* 0 = voltage 5V */ { + /* is off, turn on */ + /* turn voltage on first, = 1 */ + new = old | SCR_PHP_V; + set_scr(ice, new); + /* turn phantom on, = 0 */ + new &= ~SCR_PHP; + set_scr(ice, new); + } else if (!ucontrol->value.integer.value[0] && (old & SCR_PHP_V)) { + /* phantom off requested and 1 = voltage 48V */ + /* is on, turn off */ + /* turn voltage off first, = 0 */ + new = old & ~SCR_PHP_V; + set_scr(ice, new); + /* turn phantom off, = 1 */ + new |= SCR_PHP; + set_scr(ice, new); + } + if (old != new) + return 1; + /* no change */ + return 0; +} + +#define PRIV_SW(xid, xbit, xreg) [xid] = {.bit = xbit,\ + .set_register = set_##xreg,\ + .get_register = get_##xreg, } + + +#define PRIV_ENUM2(xid, xbit, xreg, xtext1, xtext2) [xid] = {.bit = xbit,\ + .set_register = set_##xreg,\ + .get_register = get_##xreg,\ + .texts = {xtext1, xtext2} } + +static struct qtet_kcontrol_private qtet_privates[] = { + PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"), + PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"), + PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"), + PRIV_ENUM2(COAX_OUT, CPLD_COAX_OUT, cpld, "IEC958", "I2S"), + PRIV_SW(IN12_MON12, MCR_IN12_MON12, mcr), + PRIV_SW(IN12_MON34, MCR_IN12_MON34, mcr), + PRIV_SW(IN34_MON12, MCR_IN34_MON12, mcr), + PRIV_SW(IN34_MON34, MCR_IN34_MON34, mcr), + PRIV_SW(OUT12_MON34, MCR_OUT12_MON34, mcr), + PRIV_SW(OUT34_MON12, MCR_OUT34_MON12, mcr), +}; + +static int qtet_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = ARRAY_SIZE(private.texts); + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + private.texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int qtet_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + (private.get_register(ice) & private.bit) ? 1 : 0; + return 0; +} + +static int qtet_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct qtet_kcontrol_private private = + qtet_privates[kcontrol->private_value]; + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned int old, new; + old = private.get_register(ice); + if (ucontrol->value.integer.value[0]) + new = old | private.bit; + else + new = old & ~private.bit; + if (old != new) { + private.set_register(ice, new); + return 1; + } + /* no change */ + return 0; +} + +#define qtet_sw_info snd_ctl_boolean_mono_info + +#define QTET_CONTROL(xname, xtype, xpriv) \ + {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname,\ + .info = qtet_##xtype##_info,\ + .get = qtet_sw_get,\ + .put = qtet_sw_put,\ + .private_value = xpriv } + +static struct snd_kcontrol_new qtet_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = qtet_sw_info, + .get = qtet_mute_get, + .put = qtet_mute_put, + .private_value = 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Phantom Power", + .info = qtet_sw_info, + .get = qtet_php_get, + .put = qtet_php_put, + .private_value = 0 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog In 1/2 Capture Switch", + .info = qtet_ain12_enum_info, + .get = qtet_ain12_sw_get, + .put = qtet_ain12_sw_put, + .private_value = 0 + }, + QTET_CONTROL("Analog In 3/4 Capture Switch", enum, AIN34_SEL), + QTET_CONTROL("PCM In 1/2 Capture Switch", enum, IN12_SEL), + QTET_CONTROL("PCM In 3/4 Capture Switch", enum, IN34_SEL), + QTET_CONTROL("Coax Output Source", enum, COAX_OUT), + QTET_CONTROL("Analog In 1/2 to Monitor 1/2", sw, IN12_MON12), + QTET_CONTROL("Analog In 1/2 to Monitor 3/4", sw, IN12_MON34), + QTET_CONTROL("Analog In 3/4 to Monitor 1/2", sw, IN34_MON12), + QTET_CONTROL("Analog In 3/4 to Monitor 3/4", sw, IN34_MON34), + QTET_CONTROL("Output 1/2 to Monitor 3/4", sw, OUT12_MON34), + QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12), +}; + +static char *slave_vols[] __devinitdata = { + PCM_12_PLAYBACK_VOLUME, + PCM_34_PLAYBACK_VOLUME, + NULL +}; + +static __devinitdata +DECLARE_TLV_DB_SCALE(qtet_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); + if (slave) + snd_ctl_add_slave(master, slave); + } +} + +static int __devinit qtet_add_controls(struct snd_ice1712 *ice) +{ + struct qtet_spec *spec = ice->spec; + int err, i; + struct snd_kcontrol *vmaster; + err = snd_ice1712_akm4xxx_build_controls(ice); + if (err < 0) + return err; + for (i = 0; i < ARRAY_SIZE(qtet_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&qtet_controls[i], ice)); + if (err < 0) + return err; + } + + /* Create virtual master control */ + vmaster = snd_ctl_make_virtual_master("Master Playback Volume", + qtet_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 AK4113 */ + err = snd_ak4113_build(spec->ak4113, + ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + if (err < 0) + return err; + return 0; +} + +static inline int qtet_is_spdif_master(struct snd_ice1712 *ice) +{ + /* CPLD_SYNC_SEL: 0 = internal, 1 = external (i.e. spdif master) */ + return (get_cpld(ice) & CPLD_SYNC_SEL) ? 1 : 0; +} + +static unsigned int qtet_get_rate(struct snd_ice1712 *ice) +{ + int i; + unsigned char result; + + result = get_cpld(ice) & CPLD_CKS_MASK; + for (i = 0; i < ARRAY_SIZE(cks_vals); i++) + if (cks_vals[i] == result) + return qtet_rates[i]; + return 0; +} + +static int get_cks_val(int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(qtet_rates); i++) + if (qtet_rates[i] == rate) + return cks_vals[i]; + return 0; +} + +/* setting new rate */ +static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned int new; + unsigned char val; + /* switching ice1724 to external clock - supplied by ext. circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + + new = (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate); + /* switch to internal clock, drop CPLD_SYNC_SEL */ + new &= ~CPLD_SYNC_SEL; + /* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n", + get_cpld(ice), new); */ + set_cpld(ice, new); +} + +static inline unsigned char qtet_set_mclk(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* no change in master clock */ + return 0; +} + +/* setting clock to external - SPDIF */ +static int qtet_set_spdif_clock(struct snd_ice1712 *ice, int type) +{ + unsigned int old, new; + + old = new = get_cpld(ice); + new &= ~(CPLD_CKS_MASK | CPLD_WORD_SEL); + switch (type) { + case EXT_SPDIF_TYPE: + new |= CPLD_EXT_SPDIF; + break; + case EXT_WORDCLOCK_1FS_TYPE: + new |= CPLD_EXT_WORDCLOCK_1FS; + break; + case EXT_WORDCLOCK_256FS_TYPE: + new |= CPLD_EXT_WORDCLOCK_256FS; + break; + default: + snd_BUG(); + } + if (old != new) { + set_cpld(ice, new); + /* changed */ + return 1; + } + return 0; +} + +static int qtet_get_spdif_master_type(struct snd_ice1712 *ice) +{ + unsigned int val; + int result; + val = get_cpld(ice); + /* checking only rate/clock-related bits */ + val &= (CPLD_CKS_MASK | CPLD_WORD_SEL | CPLD_SYNC_SEL); + if (!(val & CPLD_SYNC_SEL)) { + /* switched to internal clock, is not any external type */ + result = -1; + } else { + switch (val) { + case (CPLD_EXT_SPDIF): + result = EXT_SPDIF_TYPE; + break; + case (CPLD_EXT_WORDCLOCK_1FS): + result = EXT_WORDCLOCK_1FS_TYPE; + break; + case (CPLD_EXT_WORDCLOCK_256FS): + result = EXT_WORDCLOCK_256FS_TYPE; + break; + default: + /* undefined combination of external clock setup */ + snd_BUG(); + result = 0; + } + } + return result; +} + +/* Called when ak4113 detects change in the input SPDIF stream */ +static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0, + unsigned char c1) +{ + struct snd_ice1712 *ice = ak4113->change_callback_private; + int rate; + if ((qtet_get_spdif_master_type(ice) == EXT_SPDIF_TYPE) && + c1) { + /* only for SPDIF master mode, rate was changed */ + rate = snd_ak4113_external_rate(ak4113); + /* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n", + rate); */ + qtet_akm_set_rate_val(ice->akm, rate); + } +} + +/* + * If clock slaved to SPDIF-IN, setting runtime rate + * to the detected external rate + */ +static void qtet_spdif_in_open(struct snd_ice1712 *ice, + struct snd_pcm_substream *substream) +{ + struct qtet_spec *spec = ice->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int rate; + + if (qtet_get_spdif_master_type(ice) != EXT_SPDIF_TYPE) + /* not external SPDIF, no rate limitation */ + return; + /* only external SPDIF can detect incoming sample rate */ + rate = snd_ak4113_external_rate(spec->ak4113); + if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) { + runtime->hw.rate_min = rate; + runtime->hw.rate_max = rate; + } +} + +/* + * initialize the chip + */ +static int __devinit qtet_init(struct snd_ice1712 *ice) +{ + static const unsigned char ak4113_init_vals[] = { + /* AK4113_REG_PWRDN */ AK4113_RST | AK4113_PWN | + AK4113_OCKS0 | AK4113_OCKS1, + /* AK4113_REQ_FORMAT */ AK4113_DIF_I24I2S | AK4113_VTX | + AK4113_DEM_OFF | AK4113_DEAU, + /* AK4113_REG_IO0 */ AK4113_OPS2 | AK4113_TXE | + AK4113_XTL_24_576M, + /* AK4113_REG_IO1 */ AK4113_EFH_1024LRCLK | AK4113_IPS(0), + /* AK4113_REG_INT0_MASK */ 0, + /* AK4113_REG_INT1_MASK */ 0, + /* AK4113_REG_DATDTS */ 0, + }; + int err; + struct qtet_spec *spec; + struct snd_akm4xxx *ak; + unsigned char val; + + /* switching ice1724 to external clock - supplied by ext. circuits */ + val = inb(ICEMT1724(ice, RATE)); + outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + /* qtet is clocked by Xilinx array */ + ice->hw_rates = &qtet_rates_info; + ice->is_spdif_master = qtet_is_spdif_master; + ice->get_rate = qtet_get_rate; + ice->set_rate = qtet_set_rate; + ice->set_mclk = qtet_set_mclk; + ice->set_spdif_clock = qtet_set_spdif_clock; + ice->get_spdif_master_type = qtet_get_spdif_master_type; + ice->ext_clock_names = ext_clock_names; + ice->ext_clock_count = ARRAY_SIZE(ext_clock_names); + /* since Qtet can detect correct SPDIF-in rate, all streams can be + * limited to this specific rate */ + ice->spdif.ops.open = ice->pro_open = qtet_spdif_in_open; + ice->spec = spec; + + /* Mute Off */ + /* SCR Initialize*/ + /* keep codec power down first */ + set_scr(ice, SCR_PHP); + udelay(1); + /* codec power up */ + set_scr(ice, SCR_PHP | SCR_CODEC_PDN); + + /* MCR Initialize */ + set_mcr(ice, 0); + + /* CPLD Initialize */ + set_cpld(ice, 0); + + + ice->num_total_dacs = 2; + ice->num_total_adcs = 2; + + ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL); + ak = ice->akm; + if (!ak) + return -ENOMEM; + /* only one codec with two chips */ + ice->akm_codecs = 1; + err = snd_ice1712_akm4xxx_init(ak, &akm_qtet_dac, NULL, ice); + if (err < 0) + return err; + err = snd_ak4113_create(ice->card, + qtet_ak4113_read, + qtet_ak4113_write, + ak4113_init_vals, + ice, &spec->ak4113); + if (err < 0) + return err; + /* callback for codecs rate setting */ + spec->ak4113->change_callback = qtet_ak4113_change; + spec->ak4113->change_callback_private = ice; + /* AK41143 in Quartet can detect external rate correctly + * (i.e. check_flags = 0) */ + spec->ak4113->check_flags = 0; + + proc_init(ice); + + qtet_set_rate(ice, 44100); + return 0; +} + +static unsigned char qtet_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC, + 1xDACs, SPDIF in */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, in, out-ext */ + [ICE_EEP2_GPIO_DIR] = 0x00, /* 0-7 inputs, switched to output + only during output operations */ + [ICE_EEP2_GPIO_DIR1] = 0xff, /* 8-15 outputs */ + [ICE_EEP2_GPIO_DIR2] = 0x00, + [ICE_EEP2_GPIO_MASK] = 0xff, /* changed only for OUT operations */ + [ICE_EEP2_GPIO_MASK1] = 0x00, + [ICE_EEP2_GPIO_MASK2] = 0xff, + + [ICE_EEP2_GPIO_STATE] = 0x00, /* inputs */ + [ICE_EEP2_GPIO_STATE1] = 0x7d, /* all 1, but GPIO_CPLD_RW + and GPIO15 always zero */ + [ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */ +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_QTET, + .name = "Infrasonic Quartet", + .model = "quartet", + .chip_init = qtet_init, + .build_controls = qtet_add_controls, + .eeprom_size = sizeof(qtet_eeprom), + .eeprom_data = qtet_eeprom, + }, + { } /* terminator */ +}; diff --git a/sound/pci/ice1712/quartet.h b/sound/pci/ice1712/quartet.h new file mode 100644 index 000000000000..80809b72439a --- /dev/null +++ b/sound/pci/ice1712/quartet.h @@ -0,0 +1,10 @@ +#ifndef __SOUND_QTET_H +#define __SOUND_QTET_H + +#define QTET_DEVICE_DESC "{Infrasonic,Quartet}," + +#define VT1724_SUBDEVICE_QTET 0x30305349 /* Infrasonic Quartet */ + +extern struct snd_ice1712_card_info snd_vt1724_qtet_cards[]; + +#endif /* __SOUND_QTET_H */ -- cgit v1.2.2 From 87b61902ce3dec23a2d8256b9cfcf4e28786a320 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:05:18 +0200 Subject: sound: oxygen: do not try to restore nonexistent EEPROM On cards where the EEPROM was deliberately omitted, we do not need to try to restore the EEPROM's contents. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen_lib.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 9a8936e20744..c9f271419eb8 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -278,7 +278,11 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[]) static void oxygen_restore_eeprom(struct oxygen *chip, const struct pci_device_id *id) { - if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) { + u16 eeprom_id; + + eeprom_id = oxygen_read_eeprom(chip, 0); + if (eeprom_id != OXYGEN_EEPROM_ID && + (eeprom_id != 0xffff || id->subdevice != 0x8788)) { /* * This function gets called only when a known card model has * been detected, i.e., we know there is a valid subsystem -- cgit v1.2.2 From 362bc24d6746bcd49bb4853fc5aa7d4c728b3f9e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:05:58 +0200 Subject: sound: oxygen: fix for PI7C9X110 compatibility If the card is used with a Pericom PI7C9X110 PCI-E/PCI bridge, reconfigure the latter's PCI buffering to fix an unknown problem. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen_lib.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index c9f271419eb8..9c5e6450eebb 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -307,6 +307,28 @@ static void oxygen_restore_eeprom(struct oxygen *chip, } } +static void pci_bridge_magic(void) +{ + struct pci_dev *pci = NULL; + u32 tmp; + + for (;;) { + /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */ + pci = pci_get_device(0x12d8, 0xe110, pci); + if (!pci) + break; + /* + * ... configure its secondary internal arbiter to park to + * the secondary port, instead of to the last master. + */ + if (!pci_read_config_dword(pci, 0x40, &tmp)) { + tmp |= 1; + pci_write_config_dword(pci, 0x40, tmp); + } + /* Why? Try asking C-Media. */ + } +} + static void oxygen_init(struct oxygen *chip) { unsigned int i; @@ -585,6 +607,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, snd_card_set_dev(card, &pci->dev); card->private_free = oxygen_card_free; + pci_bridge_magic(); oxygen_init(chip); chip->model.init(chip); -- cgit v1.2.2 From 65c3ac885ce9852852b895a4a62212f62cb5f2e9 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:11:27 +0200 Subject: sound: virtuoso: split virtuoso.c The virtuoso.c file has become rather big. This patch splits it up so that only code for very similar card models is in one file. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/Makefile | 3 +- sound/pci/oxygen/virtuoso.c | 1105 +------------------------------------- sound/pci/oxygen/xonar.h | 50 ++ sound/pci/oxygen/xonar_cs43xx.c | 304 +++++++++++ sound/pci/oxygen/xonar_hdmi.c | 128 +++++ sound/pci/oxygen/xonar_lib.c | 132 +++++ sound/pci/oxygen/xonar_pcm179x.c | 660 +++++++++++++++++++++++ 7 files changed, 1290 insertions(+), 1092 deletions(-) create mode 100644 sound/pci/oxygen/xonar.h create mode 100644 sound/pci/oxygen/xonar_cs43xx.c create mode 100644 sound/pci/oxygen/xonar_hdmi.c create mode 100644 sound/pci/oxygen/xonar_lib.c create mode 100644 sound/pci/oxygen/xonar_pcm179x.c (limited to 'sound/pci') diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 4ba07d42fd1d..389941cf6100 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -1,7 +1,8 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o snd-hifier-objs := hifier.o snd-oxygen-objs := oxygen.o -snd-virtuoso-objs := virtuoso.o +snd-virtuoso-objs := virtuoso.o xonar_lib.o \ + xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o obj-$(CONFIG_SND_HIFIER) += snd-hifier.o diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 6ebcb6bdd712..6accaf9580b2 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -17,145 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * Xonar D2/D2X - * ------------ - * - * CMI8788: - * - * SPI 0 -> 1st PCM1796 (front) - * SPI 1 -> 2nd PCM1796 (surround) - * SPI 2 -> 3rd PCM1796 (center/LFE) - * SPI 4 -> 4th PCM1796 (back) - * - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 5 <- external power present (D2X only) - * GPIO 7 -> ALT - * GPIO 8 -> enable output to speakers - */ - -/* - * Xonar D1/DX - * ----------- - * - * CMI8788: - * - * I²C <-> CS4398 (front) - * <-> CS4362A (surround, center/LFE, back) - * - * GPI 0 <- external power present (DX only) - * - * 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) - * - * CS4398: - * - * AD0 <- 1 - * AD1 <- 1 - * - * CS4362A: - * - * AD0 <- 0 - */ - -/* - * Xonar HDAV1.3 (Deluxe) - * ---------------------- - * - * CMI8788: - * - * I²C <-> PCM1796 (front) - * - * GPI 0 <- external power present - * - * GPIO 0 -> enable output to speakers - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) - * - * TXD -> HDMI controller - * RXD <- HDMI controller - * - * PCM1796 front: AD1,0 <- 0,0 - * - * no daughterboard - * ---------------- - * - * GPIO 4 <- 1 - * - * H6 daughterboard - * ---------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 0 - * - * I²C <-> PCM1796 (surround) - * <-> PCM1796 (center/LFE) - * <-> PCM1796 (back) - * - * PCM1796 surround: AD1,0 <- 0,1 - * PCM1796 center/LFE: AD1,0 <- 1,0 - * PCM1796 back: AD1,0 <- 1,1 - * - * unknown daughterboard - * --------------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 1 - * - * I²C <-> CS4362A (surround, center/LFE, back) - * - * CS4362A: AD0 <- 0 - */ - -/* - * Xonar Essence ST (Deluxe)/STX - * ----------------------------- - * - * CMI8788: - * - * I²C <-> PCM1792A - * - * GPI 0 <- external power present - * - * GPIO 0 -> enable output to speakers - * GPIO 1 -> route HP to front panel (0) or rear jack (1) - * GPIO 2 -> M0 of CS5381 - * GPIO 3 -> M1 of CS5381 - * GPIO 7 -> route output to speaker jacks (0) or HP (1) - * GPIO 8 -> route input jack to line-in (0) or mic-in (1) - * - * PCM1792A: - * - * AD0 <- 0 - * - * H6 daughterboard - * ---------------- - * - * GPIO 4 <- 0 - * GPIO 5 <- 0 - */ - #include #include -#include -#include -#include -#include #include #include #include -#include -#include -#include "oxygen.h" -#include "cm9780.h" -#include "pcm1796.h" -#include "cs4398.h" -#include "cs4362a.h" +#include "xonar.h" MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("Asus AVx00 driver"); @@ -173,972 +40,28 @@ 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_D1, - MODEL_DX, - MODEL_HDAV, /* without daughterboard */ - MODEL_HDAV_H6, /* with H6 daughterboard */ - MODEL_ST, - MODEL_ST_H6, - MODEL_STX, -}; - static struct pci_device_id xonar_ids[] __devinitdata = { - { 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 }, - { OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV }, - { OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX }, - { OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 }, - { OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX }, - { OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST }, + { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8275) }, + { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8314) }, + { OXYGEN_PCI_SUBID(0x1043, 0x8327) }, + { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, + { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } }; MODULE_DEVICE_TABLE(pci, xonar_ids); - -#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 GPIO_DB_MASK 0x0030 -#define GPIO_DB_H6 0x0000 -#define GPIO_DB_XX 0x0020 - -#define GPIO_ST_HP_REAR 0x0002 -#define GPIO_ST_HP 0x0080 - -#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ADx=i, /W=0 */ -#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 { - unsigned int anti_pop_delay; - unsigned int dacs; - u16 output_enable_bit; - u8 ext_power_reg; - u8 ext_power_int_reg; - u8 ext_power_bit; - u8 has_power; - u8 pcm1796_oversampling; - u8 cs4398_fm; - u8 cs4362a_fm; - u8 hdmi_params[5]; -}; - -static void xonar_gpio_changed(struct oxygen *chip); - -static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - /* maps ALSA channel pair number to SPI output */ - static const u8 codec_map[4] = { - 0, 1, 2, 4 - }; - oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | - OXYGEN_SPI_DATA_LENGTH_2 | - OXYGEN_SPI_CLOCK_160 | - (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | - OXYGEN_SPI_CEN_LATCH_CLOCK_HI, - (reg << 8) | value); -} - -static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value); -} - -static void pcm1796_write(struct oxygen *chip, unsigned int codec, - u8 reg, u8 value) -{ - if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == - OXYGEN_FUNCTION_SPI) - pcm1796_write_spi(chip, codec, reg, value); - else - pcm1796_write_i2c(chip, codec, reg, value); -} - -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 hdmi_write_command(struct oxygen *chip, u8 command, - unsigned int count, const u8 *params) -{ - unsigned int i; - u8 checksum; - - oxygen_write_uart(chip, 0xfb); - oxygen_write_uart(chip, 0xef); - oxygen_write_uart(chip, command); - oxygen_write_uart(chip, count); - for (i = 0; i < count; ++i) - oxygen_write_uart(chip, params[i]); - checksum = 0xfb + 0xef + command + count; - for (i = 0; i < count; ++i) - checksum += params[i]; - oxygen_write_uart(chip, checksum); -} - -static void xonar_enable_output(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - msleep(data->anti_pop_delay); - oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); -} - -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; - chip->model.gpio_changed = xonar_gpio_changed; - 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 | data->output_enable_bit); - 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); - xonar_enable_output(chip); -} - -static void update_pcm1796_volume(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); - } -} - -static void update_pcm1796_mute(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - u8 value; - - value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; - if (chip->dac_mute) - value |= PCM1796_MUTE; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 18, value); -} - -static void pcm1796_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); - pcm1796_write(chip, i, 20, data->pcm1796_oversampling); - pcm1796_write(chip, i, 21, 0); - } - update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ - update_pcm1796_volume(chip); -} - -static void xonar_d2_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->anti_pop_delay = 300; - data->dacs = 4; - data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - 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 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 cs43xx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - /* 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, data->cs4398_fm); - cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); - 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, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); - update_cs43xx_volume(chip); - update_cs43xx_mute(chip); - /* clear power down */ - cs4398_write(chip, 8, CS4398_CPEN); - cs4362a_write(chip, 0x01, CS4362A_CPEN); -} - -static void xonar_d1_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->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_FM_SINGLE | - CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - cs43xx_init(chip); - - 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_dx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; - - xonar_d1_init(chip); -} - -static void xonar_hdav_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 param; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - data->anti_pop_delay = 100; - data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1; - 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; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE); - - oxygen_reset_uart(chip); - param = 0; - hdmi_write_command(chip, 0x61, 1, ¶m); - param = 1; - hdmi_write_command(chip, 0x74, 1, ¶m); - data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; - data->hdmi_params[4] = 1; - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); - - xonar_common_init(chip); - - snd_component_add(chip->card, "PCM1796"); - snd_component_add(chip->card, "CS5381"); -} - -static void xonar_st_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, - OXYGEN_2WIRE_LENGTH_8 | - OXYGEN_2WIRE_INTERRUPT_MASK | - OXYGEN_2WIRE_SPEED_FAST); - - if (chip->model.private_data == MODEL_ST_H6) - chip->model.dac_channels = 8; - data->anti_pop_delay = 100; - data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1; - data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE; - data->pcm1796_oversampling = PCM1796_OS_64; - - pcm1796_init(chip); - - oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, - GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, - GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); - - xonar_common_init(chip); - - snd_component_add(chip->card, "PCM1792A"); - snd_component_add(chip->card, "CS5381"); -} - -static void xonar_stx_init(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - data->ext_power_reg = OXYGEN_GPI_DATA; - data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; - data->ext_power_bit = GPI_DX_EXT_POWER; - - xonar_st_init(chip); -} - -static void xonar_disable_output(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - - oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); -} - -static void xonar_d2_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); -} - -static void xonar_d1_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); - cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); - oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); -} - -static void xonar_hdav_cleanup(struct oxygen *chip) -{ - u8 param = 0; - - hdmi_write_command(chip, 0x74, 1, ¶m); - xonar_disable_output(chip); -} - -static void xonar_st_cleanup(struct oxygen *chip) -{ - xonar_disable_output(chip); -} - -static void xonar_d2_suspend(struct oxygen *chip) -{ - xonar_d2_cleanup(chip); -} - -static void xonar_d1_suspend(struct oxygen *chip) -{ - xonar_d1_cleanup(chip); -} - -static void xonar_hdav_suspend(struct oxygen *chip) -{ - xonar_hdav_cleanup(chip); - msleep(2); -} - -static void xonar_st_suspend(struct oxygen *chip) -{ - xonar_st_cleanup(chip); -} - -static void xonar_d2_resume(struct oxygen *chip) -{ - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_d1_resume(struct oxygen *chip) -{ - oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); - msleep(1); - cs43xx_init(chip); - xonar_enable_output(chip); -} - -static void xonar_hdav_resume(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 param; - - oxygen_reset_uart(chip); - param = 0; - hdmi_write_command(chip, 0x61, 1, ¶m); - param = 1; - hdmi_write_command(chip, 0x74, 1, ¶m); - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_st_resume(struct oxygen *chip) -{ - pcm1796_init(chip); - xonar_enable_output(chip); -} - -static void xonar_hdav_pcm_hardware_filter(unsigned int channel, - struct snd_pcm_hardware *hardware) -{ - if (channel == PCM_MULTICH) { - hardware->rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000; - hardware->rate_min = 44100; - } -} - -static void set_pcm1796_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - unsigned int i; - - data->pcm1796_oversampling = - params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 20, data->pcm1796_oversampling); -} - -static void set_cs53x1_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - unsigned int value; - - if (params_rate(params) <= 54000) - value = GPIO_CS53x1_M_SINGLE; - else if (params_rate(params) <= 108000) - value = GPIO_CS53x1_M_DOUBLE; - else - value = GPIO_CS53x1_M_QUAD; - oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, - value, GPIO_CS53x1_M_MASK); -} - -static void set_cs43xx_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - if (params_rate(params) <= 50000) { - data->cs4398_fm |= CS4398_FM_SINGLE; - data->cs4362a_fm |= CS4362A_FM_SINGLE; - } else if (params_rate(params) <= 100000) { - data->cs4398_fm |= CS4398_FM_DOUBLE; - data->cs4362a_fm |= CS4362A_FM_DOUBLE; - } else { - data->cs4398_fm |= CS4398_FM_QUAD; - data->cs4362a_fm |= CS4362A_FM_QUAD; - } - cs4398_write(chip, 2, data->cs4398_fm); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); -} - -static void set_hdmi_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - struct xonar_data *data = chip->model_data; - - data->hdmi_params[0] = 0; /* 1 = non-audio */ - switch (params_rate(params)) { - case 44100: - data->hdmi_params[1] = IEC958_AES3_CON_FS_44100; - break; - case 48000: - data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; - break; - default: /* 96000 */ - data->hdmi_params[1] = IEC958_AES3_CON_FS_96000; - break; - case 192000: - data->hdmi_params[1] = IEC958_AES3_CON_FS_192000; - break; - } - data->hdmi_params[2] = params_channels(params) / 2 - 1; - if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) - data->hdmi_params[3] = 0; - else - data->hdmi_params[3] = 0xc0; - data->hdmi_params[4] = 1; /* ? */ - hdmi_write_command(chip, 0x54, 5, data->hdmi_params); -} - -static void set_hdav_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) -{ - set_pcm1796_params(chip, params); - set_hdmi_params(chip, params); -} - -static void xonar_gpio_changed(struct oxygen *chip) -{ - struct xonar_data *data = chip->model_data; - u8 has_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) { - snd_printk(KERN_NOTICE "power restored\n"); - } else { - snd_printk(KERN_CRIT - "Hey! Don't unplug the power cable!\n"); - /* TODO: stop PCMs */ - } - } -} - -static void xonar_hdav_uart_input(struct oxygen *chip) -{ - if (chip->uart_input_count >= 2 && - chip->uart_input[chip->uart_input_count - 2] == 'O' && - chip->uart_input[chip->uart_input_count - 1] == 'K') { - printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:\n"); - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, - chip->uart_input, chip->uart_input_count); - chip->uart_input_count = 0; - } -} - -static int gpio_bit_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 bit = ctl->private_value; - - value->value.integer.value[0] = - !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); - return 0; -} - -static int gpio_bit_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 bit = ctl->private_value; - u16 old_bits, new_bits; - int changed; - - spin_lock_irq(&chip->reg_lock); - old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); - if (value->value.integer.value[0]) - new_bits = old_bits | bit; - else - new_bits = old_bits & ~bit; - changed = new_bits != old_bits; - if (changed) - oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); - spin_unlock_irq(&chip->reg_lock); - return changed; -} - -static const struct snd_kcontrol_new alt_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Loopback Switch", - .info = snd_ctl_boolean_mono_info, - .get = gpio_bit_switch_get, - .put = gpio_bit_switch_put, - .private_value = GPIO_D2_ALT, -}; - -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 = gpio_bit_switch_get, - .put = gpio_bit_switch_put, - .private_value = GPIO_DX_FRONT_PANEL, -}; - -static int st_output_switch_info(struct snd_kcontrol *ctl, - struct snd_ctl_elem_info *info) -{ - static const char *const names[3] = { - "Speakers", "Headphones", "FP Headphones" - }; - - info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - info->count = 1; - info->value.enumerated.items = 3; - if (info->value.enumerated.item >= 3) - info->value.enumerated.item = 2; - strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); - return 0; -} - -static int st_output_switch_get(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 gpio; - - gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); - if (!(gpio & GPIO_ST_HP)) - value->value.enumerated.item[0] = 0; - else if (gpio & GPIO_ST_HP_REAR) - value->value.enumerated.item[0] = 1; - else - value->value.enumerated.item[0] = 2; - return 0; -} - - -static int st_output_switch_put(struct snd_kcontrol *ctl, - struct snd_ctl_elem_value *value) -{ - struct oxygen *chip = ctl->private_data; - u16 gpio_old, gpio; - - mutex_lock(&chip->mutex); - gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); - gpio = gpio_old; - switch (value->value.enumerated.item[0]) { - case 0: - gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR); - break; - case 1: - gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR; - break; - case 2: - gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR; - break; - } - oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); - mutex_unlock(&chip->mutex); - return gpio != gpio_old; -} - -static const struct snd_kcontrol_new st_output_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output", - .info = st_output_switch_info, - .get = st_output_switch_get, - .put = st_output_switch_put, -}; - -static void xonar_line_mic_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, -6000, 50, 0); -static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); - -static int xonar_d2_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - /* CD in is actually connected to the video in pin */ - template->private_value ^= AC97_CD ^ AC97_VIDEO; - return 0; -} - -static int xonar_d1_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - return 0; -} - -static int xonar_st_control_filter(struct snd_kcontrol_new *template) -{ - if (!strncmp(template->name, "CD Capture ", 11)) - return 1; /* no CD input */ - if (!strcmp(template->name, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ - return 0; -} - -static int xonar_d2_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); -} - -static int xonar_d1_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); -} - -static int xonar_st_mixer_init(struct oxygen *chip) -{ - return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); -} - -static const struct oxygen_model model_xonar_d2 = { - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .init = xonar_d2_init, - .control_filter = xonar_d2_control_filter, - .mixer_init = xonar_d2_mixer_init, - .cleanup = xonar_d2_cleanup, - .suspend = xonar_d2_suspend, - .resume = xonar_d2_resume, - .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), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF | - MIDI_OUTPUT | - MIDI_INPUT, - .dac_channels = 8, - .dac_volume_min = 255 - 2*60, - .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, -}; - -static const struct oxygen_model model_xonar_d1 = { - .longname = "Asus Virtuoso 100", - .chip = "AV200", - .init = xonar_d1_init, - .control_filter = xonar_d1_control_filter, - .mixer_init = xonar_d1_mixer_init, - .cleanup = xonar_d1_cleanup, - .suspend = xonar_d1_suspend, - .resume = xonar_d1_resume, - .set_dac_params = set_cs43xx_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_cs43xx_volume, - .update_dac_mute = update_cs43xx_mute, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = cs4362a_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2, - .dac_channels = 8, - .dac_volume_min = 127 - 60, - .dac_volume_max = 127, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - -static const struct oxygen_model model_xonar_hdav = { - .longname = "Asus Virtuoso 200", - .chip = "AV200", - .init = xonar_hdav_init, - .cleanup = xonar_hdav_cleanup, - .suspend = xonar_hdav_suspend, - .resume = xonar_hdav_resume, - .pcm_hardware_filter = xonar_hdav_pcm_hardware_filter, - .set_dac_params = set_hdav_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .uart_input = xonar_hdav_uart_input, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = pcm1796_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2 | - CAPTURE_1_FROM_SPDIF, - .dac_channels = 8, - .dac_volume_min = 255 - 2*60, - .dac_volume_max = 255, - .misc_flags = OXYGEN_MISC_MIDI, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - -static const struct oxygen_model model_xonar_st = { - .longname = "Asus Virtuoso 100", - .chip = "AV200", - .init = xonar_st_init, - .control_filter = xonar_st_control_filter, - .mixer_init = xonar_st_mixer_init, - .cleanup = xonar_st_cleanup, - .suspend = xonar_st_suspend, - .resume = xonar_st_resume, - .set_dac_params = set_pcm1796_params, - .set_adc_params = set_cs53x1_params, - .update_dac_volume = update_pcm1796_volume, - .update_dac_mute = update_pcm1796_mute, - .ac97_switch = xonar_line_mic_ac97_switch, - .dac_tlv = pcm1796_db_scale, - .model_data_size = sizeof(struct xonar_data), - .device_config = PLAYBACK_0_TO_I2S | - PLAYBACK_1_TO_SPDIF | - CAPTURE_0_FROM_I2S_2, - .dac_channels = 2, - .dac_volume_min = 255 - 2*60, - .dac_volume_max = 255, - .function_flags = OXYGEN_FUNCTION_2WIRE, - .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, - .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; - static int __devinit get_xonar_model(struct oxygen *chip, const struct pci_device_id *id) { - static const struct oxygen_model *const models[] = { - [MODEL_D1] = &model_xonar_d1, - [MODEL_DX] = &model_xonar_d1, - [MODEL_D2] = &model_xonar_d2, - [MODEL_D2X] = &model_xonar_d2, - [MODEL_HDAV] = &model_xonar_hdav, - [MODEL_ST] = &model_xonar_st, - [MODEL_STX] = &model_xonar_st, - }; - static const char *const names[] = { - [MODEL_D1] = "Xonar D1", - [MODEL_DX] = "Xonar DX", - [MODEL_D2] = "Xonar D2", - [MODEL_D2X] = "Xonar D2X", - [MODEL_HDAV] = "Xonar HDAV1.3", - [MODEL_HDAV_H6] = "Xonar HDAV1.3+H6", - [MODEL_ST] = "Xonar Essence ST", - [MODEL_ST_H6] = "Xonar Essence ST+H6", - [MODEL_STX] = "Xonar Essence STX", - }; - unsigned int model = id->driver_data; - - if (model >= ARRAY_SIZE(models) || !models[model]) - return -EINVAL; - chip->model = *models[model]; - - switch (model) { - case MODEL_D2X: - chip->model.init = xonar_d2x_init; - break; - case MODEL_DX: - chip->model.init = xonar_dx_init; - break; - case MODEL_HDAV: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { - case GPIO_DB_H6: - model = MODEL_HDAV_H6; - break; - case GPIO_DB_XX: - snd_printk(KERN_ERR "unknown daughterboard\n"); - return -ENODEV; - } - break; - case MODEL_ST: - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { - case GPIO_DB_H6: - model = MODEL_ST_H6; - break; - } - break; - case MODEL_STX: - chip->model.init = xonar_stx_init; - oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); - break; - } - - chip->model.shortname = names[model]; - chip->model.private_data = model; - return 0; + if (get_xonar_pcm179x_model(chip, id) >= 0) + return 0; + if (get_xonar_cs43xx_model(chip, id) >= 0) + return 0; + return -EINVAL; } static int __devinit xonar_probe(struct pci_dev *pci, diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h new file mode 100644 index 000000000000..89b3ed814d64 --- /dev/null +++ b/sound/pci/oxygen/xonar.h @@ -0,0 +1,50 @@ +#ifndef XONAR_H_INCLUDED +#define XONAR_H_INCLUDED + +#include "oxygen.h" + +struct xonar_generic { + 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; +}; + +struct xonar_hdmi { + u8 params[5]; +}; + +/* generic helper functions */ + +void xonar_enable_output(struct oxygen *chip); +void xonar_disable_output(struct oxygen *chip); +void xonar_init_ext_power(struct oxygen *chip); +void xonar_init_cs53x1(struct oxygen *chip); +void xonar_set_cs53x1_params(struct oxygen *chip, + struct snd_pcm_hw_params *params); +int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value); +int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value); + +/* model-specific card drivers */ + +int get_xonar_pcm179x_model(struct oxygen *chip, + const struct pci_device_id *id); +int get_xonar_cs43xx_model(struct oxygen *chip, + const struct pci_device_id *id); + +/* HDMI helper functions */ + +void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data); +void xonar_hdmi_cleanup(struct oxygen *chip); +void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi); +void xonar_hdmi_pcm_hardware_filter(unsigned int channel, + struct snd_pcm_hardware *hardware); +void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi, + struct snd_pcm_hw_params *params); +void xonar_hdmi_uart_input(struct oxygen *chip); + +#endif diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c new file mode 100644 index 000000000000..8fb5797577dd --- /dev/null +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -0,0 +1,304 @@ +/* + * card driver for models with CS4398/CS4362A DACs (Xonar D1/DX) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +/* + * Xonar D1/DX + * ----------- + * + * CMI8788: + * + * I²C <-> CS4398 (front) + * <-> CS4362A (surround, center/LFE, back) + * + * GPI 0 <- external power present (DX only) + * + * 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) + * + * CS4398: + * + * AD0 <- 1 + * AD1 <- 1 + * + * CS4362A: + * + * AD0 <- 0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" +#include "cs4398.h" +#include "cs4362a.h" + +#define GPI_EXT_POWER 0x01 +#define GPIO_D1_OUTPUT_ENABLE 0x0001 +#define GPIO_D1_FRONT_PANEL 0x0002 +#define GPIO_D1_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_cs43xx { + struct xonar_generic generic; + u8 cs4398_fm; + u8 cs4362a_fm; +}; + +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 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 cs43xx_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + /* 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, data->cs4398_fm); + cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + 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, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x0c, data->cs4362a_fm); + update_cs43xx_volume(chip); + update_cs43xx_mute(chip); + /* clear power down */ + cs4398_write(chip, 8, CS4398_CPEN); + cs4362a_write(chip, 0x01, CS4362A_CPEN); +} + +static void xonar_d1_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->generic.anti_pop_delay = 800; + data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE; + data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4362a_fm = CS4362A_FM_SINGLE | + CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + cs43xx_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "CS4398"); + snd_component_add(chip->card, "CS4362A"); + snd_component_add(chip->card, "CS5361"); +} + +static void xonar_dx_init(struct oxygen *chip) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPI_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->generic.ext_power_bit = GPI_EXT_POWER; + xonar_init_ext_power(chip); + xonar_d1_init(chip); +} + +static void xonar_d1_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); + cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN); + oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); +} + +static void xonar_d1_suspend(struct oxygen *chip) +{ + xonar_d1_cleanup(chip); +} + +static void xonar_d1_resume(struct oxygen *chip) +{ + oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); + msleep(1); + cs43xx_init(chip); + xonar_enable_output(chip); +} + +static void set_cs43xx_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + if (params_rate(params) <= 50000) { + data->cs4398_fm |= CS4398_FM_SINGLE; + data->cs4362a_fm |= CS4362A_FM_SINGLE; + } else if (params_rate(params) <= 100000) { + data->cs4398_fm |= CS4398_FM_DOUBLE; + data->cs4362a_fm |= CS4362A_FM_DOUBLE; + } else { + data->cs4398_fm |= CS4398_FM_QUAD; + data->cs4362a_fm |= CS4362A_FM_QUAD; + } + cs4398_write(chip, 2, data->cs4398_fm); + cs4362a_write(chip, 0x06, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x0c, data->cs4362a_fm); +} + +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 = xonar_gpio_bit_switch_get, + .put = xonar_gpio_bit_switch_put, + .private_value = GPIO_D1_FRONT_PANEL, +}; + +static void xonar_d1_line_mic_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_D1_INPUT_ROUTE : 0, + GPIO_D1_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + +static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0); + +static int xonar_d1_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + return 0; +} + +static int xonar_d1_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); +} + +static const struct oxygen_model model_xonar_d1 = { + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .init = xonar_d1_init, + .control_filter = xonar_d1_control_filter, + .mixer_init = xonar_d1_mixer_init, + .cleanup = xonar_d1_cleanup, + .suspend = xonar_d1_suspend, + .resume = xonar_d1_resume, + .set_dac_params = set_cs43xx_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_cs43xx_volume, + .update_dac_mute = update_cs43xx_mute, + .ac97_switch = xonar_d1_line_mic_ac97_switch, + .dac_tlv = cs4362a_db_scale, + .model_data_size = sizeof(struct xonar_cs43xx), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 8, + .dac_volume_min = 127 - 60, + .dac_volume_max = 127, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int __devinit get_xonar_cs43xx_model(struct oxygen *chip, + const struct pci_device_id *id) +{ + switch (id->subdevice) { + case 0x834f: + chip->model = model_xonar_d1; + chip->model.shortname = "Xonar D1"; + break; + case 0x8275: + case 0x8327: + chip->model = model_xonar_d1; + chip->model.shortname = "Xonar DX"; + chip->model.init = xonar_dx_init; + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/sound/pci/oxygen/xonar_hdmi.c b/sound/pci/oxygen/xonar_hdmi.c new file mode 100644 index 000000000000..b12db1f1cea9 --- /dev/null +++ b/sound/pci/oxygen/xonar_hdmi.c @@ -0,0 +1,128 @@ +/* + * helper functions for HDMI models (Xonar HDAV1.3) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" + +static void hdmi_write_command(struct oxygen *chip, u8 command, + unsigned int count, const u8 *params) +{ + unsigned int i; + u8 checksum; + + oxygen_write_uart(chip, 0xfb); + oxygen_write_uart(chip, 0xef); + oxygen_write_uart(chip, command); + oxygen_write_uart(chip, count); + for (i = 0; i < count; ++i) + oxygen_write_uart(chip, params[i]); + checksum = 0xfb + 0xef + command + count; + for (i = 0; i < count; ++i) + checksum += params[i]; + oxygen_write_uart(chip, checksum); +} + +static void xonar_hdmi_init_commands(struct oxygen *chip, + struct xonar_hdmi *hdmi) +{ + u8 param; + + oxygen_reset_uart(chip); + param = 0; + hdmi_write_command(chip, 0x61, 1, ¶m); + param = 1; + hdmi_write_command(chip, 0x74, 1, ¶m); + hdmi_write_command(chip, 0x54, 5, hdmi->params); +} + +void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi) +{ + hdmi->params[1] = IEC958_AES3_CON_FS_48000; + hdmi->params[4] = 1; + xonar_hdmi_init_commands(chip, hdmi); +} + +void xonar_hdmi_cleanup(struct oxygen *chip) +{ + u8 param = 0; + + hdmi_write_command(chip, 0x74, 1, ¶m); +} + +void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi) +{ + xonar_hdmi_init_commands(chip, hdmi); +} + +void xonar_hdmi_pcm_hardware_filter(unsigned int channel, + struct snd_pcm_hardware *hardware) +{ + if (channel == PCM_MULTICH) { + hardware->rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000; + hardware->rate_min = 44100; + } +} + +void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi, + struct snd_pcm_hw_params *params) +{ + hdmi->params[0] = 0; /* 1 = non-audio */ + switch (params_rate(params)) { + case 44100: + hdmi->params[1] = IEC958_AES3_CON_FS_44100; + break; + case 48000: + hdmi->params[1] = IEC958_AES3_CON_FS_48000; + break; + default: /* 96000 */ + hdmi->params[1] = IEC958_AES3_CON_FS_96000; + break; + case 192000: + hdmi->params[1] = IEC958_AES3_CON_FS_192000; + break; + } + hdmi->params[2] = params_channels(params) / 2 - 1; + if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) + hdmi->params[3] = 0; + else + hdmi->params[3] = 0xc0; + hdmi->params[4] = 1; /* ? */ + hdmi_write_command(chip, 0x54, 5, hdmi->params); +} + +void xonar_hdmi_uart_input(struct oxygen *chip) +{ + if (chip->uart_input_count >= 2 && + chip->uart_input[chip->uart_input_count - 2] == 'O' && + chip->uart_input[chip->uart_input_count - 1] == 'K') { + printk(KERN_DEBUG "message from HDMI chip received:\n"); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, + chip->uart_input, chip->uart_input_count); + chip->uart_input_count = 0; + } +} diff --git a/sound/pci/oxygen/xonar_lib.c b/sound/pci/oxygen/xonar_lib.c new file mode 100644 index 000000000000..b3ff71316653 --- /dev/null +++ b/sound/pci/oxygen/xonar_lib.c @@ -0,0 +1,132 @@ +/* + * helper functions for Asus Xonar cards + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +#include +#include +#include +#include +#include +#include "xonar.h" + + +#define GPIO_CS53x1_M_MASK 0x000c +#define GPIO_CS53x1_M_SINGLE 0x0000 +#define GPIO_CS53x1_M_DOUBLE 0x0004 +#define GPIO_CS53x1_M_QUAD 0x0008 + + +void xonar_enable_output(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit); + msleep(data->anti_pop_delay); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +void xonar_disable_output(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit); +} + +static void xonar_ext_power_gpio_changed(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + u8 has_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) { + snd_printk(KERN_NOTICE "power restored\n"); + } else { + snd_printk(KERN_CRIT + "Hey! Don't unplug the power cable!\n"); + /* TODO: stop PCMs */ + } + } +} + +void xonar_init_ext_power(struct oxygen *chip) +{ + struct xonar_generic *data = chip->model_data; + + oxygen_set_bits8(chip, data->ext_power_int_reg, + data->ext_power_bit); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + chip->model.gpio_changed = xonar_ext_power_gpio_changed; + data->has_power = !!(oxygen_read8(chip, data->ext_power_reg) + & data->ext_power_bit); +} + +void xonar_init_cs53x1(struct oxygen *chip) +{ + 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); +} + +void xonar_set_cs53x1_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_CS53x1_M_SINGLE; + else if (params_rate(params) <= 108000) + value = GPIO_CS53x1_M_DOUBLE; + else + value = GPIO_CS53x1_M_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_CS53x1_M_MASK); +} + +int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 bit = ctl->private_value; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit); + return 0; +} + +int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 bit = ctl->private_value; + u16 old_bits, new_bits; + int changed; + + spin_lock_irq(&chip->reg_lock); + old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_bits = old_bits | bit; + else + new_bits = old_bits & ~bit; + changed = new_bits != old_bits; + if (changed) + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); + spin_unlock_irq(&chip->reg_lock); + return changed; +} diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c new file mode 100644 index 000000000000..eb5f015fcd23 --- /dev/null +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -0,0 +1,660 @@ +/* + * card driver for models with PCM1796 DACs (Xonar D2/D2X/HDAV1.3/ST/STX) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +/* + * Xonar D2/D2X + * ------------ + * + * CMI8788: + * + * SPI 0 -> 1st PCM1796 (front) + * SPI 1 -> 2nd PCM1796 (surround) + * SPI 2 -> 3rd PCM1796 (center/LFE) + * SPI 4 -> 4th PCM1796 (back) + * + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 5 <- external power present (D2X only) + * GPIO 7 -> ALT + * GPIO 8 -> enable output to speakers + */ + +/* + * Xonar HDAV1.3 (Deluxe) + * ---------------------- + * + * CMI8788: + * + * I²C <-> PCM1796 (front) + * + * GPI 0 <- external power present + * + * GPIO 0 -> enable output to speakers + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * TXD -> HDMI controller + * RXD <- HDMI controller + * + * PCM1796 front: AD1,0 <- 0,0 + * + * no daughterboard + * ---------------- + * + * GPIO 4 <- 1 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + * + * I²C <-> PCM1796 (surround) + * <-> PCM1796 (center/LFE) + * <-> PCM1796 (back) + * + * PCM1796 surround: AD1,0 <- 0,1 + * PCM1796 center/LFE: AD1,0 <- 1,0 + * PCM1796 back: AD1,0 <- 1,1 + * + * unknown daughterboard + * --------------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 1 + * + * I²C <-> CS4362A (surround, center/LFE, back) + * + * CS4362A: AD0 <- 0 + */ + +/* + * Xonar Essence ST (Deluxe)/STX + * ----------------------------- + * + * CMI8788: + * + * I²C <-> PCM1792A + * + * GPI 0 <- external power present (STX only) + * + * GPIO 0 -> enable output to speakers + * GPIO 1 -> route HP to front panel (0) or rear jack (1) + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 7 -> route output to speaker jacks (0) or HP (1) + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * PCM1792A: + * + * AD1,0 <- 0,0 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" +#include "cm9780.h" +#include "pcm1796.h" + + +#define GPIO_D2X_EXT_POWER 0x0020 +#define GPIO_D2_ALT 0x0080 +#define GPIO_D2_OUTPUT_ENABLE 0x0100 + +#define GPI_EXT_POWER 0x01 +#define GPIO_INPUT_ROUTE 0x0100 + +#define GPIO_HDAV_OUTPUT_ENABLE 0x0001 + +#define GPIO_DB_MASK 0x0030 +#define GPIO_DB_H6 0x0000 + +#define GPIO_ST_OUTPUT_ENABLE 0x0001 +#define GPIO_ST_HP_REAR 0x0002 +#define GPIO_ST_HP 0x0080 + +#define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ + + +struct xonar_pcm179x { + struct xonar_generic generic; + unsigned int dacs; + u8 oversampling; +}; + +struct xonar_hdav { + struct xonar_pcm179x pcm179x; + struct xonar_hdmi hdmi; +}; + + +static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + (reg << 8) | value); +} + +static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value); +} + +static void pcm1796_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == + OXYGEN_FUNCTION_SPI) + pcm1796_write_spi(chip, codec, reg, value); + else + pcm1796_write_i2c(chip, codec, reg, value); +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + u8 value; + + value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 18, value); +} + +static void pcm1796_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write(chip, i, 21, 0); + } + update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ + update_pcm1796_volume(chip); +} + +static void xonar_d2_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.anti_pop_delay = 300; + data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE; + data->dacs = 4; + data->oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT); + + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_d2x_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPIO_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; + data->generic.ext_power_bit = GPIO_D2X_EXT_POWER; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); + xonar_init_ext_power(chip); + xonar_d2_init(chip); +} + +static void xonar_hdav_init(struct oxygen *chip) +{ + struct xonar_hdav *data = chip->model_data; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + data->pcm179x.generic.anti_pop_delay = 100; + data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE; + data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA; + data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; + data->pcm179x.dacs = chip->model.private_data ? 4 : 1; + data->pcm179x.oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE); + + xonar_init_cs53x1(chip); + xonar_init_ext_power(chip); + xonar_hdmi_init(chip, &data->hdmi); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_st_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, + OXYGEN_2WIRE_LENGTH_8 | + OXYGEN_2WIRE_INTERRUPT_MASK | + OXYGEN_2WIRE_SPEED_FAST); + + data->generic.anti_pop_delay = 100; + data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; + data->dacs = chip->model.private_data ? 4 : 1; + data->oversampling = PCM1796_OS_64; + + pcm1796_init(chip); + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, + GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP); + + xonar_init_cs53x1(chip); + xonar_enable_output(chip); + + snd_component_add(chip->card, "PCM1792A"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_stx_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->generic.ext_power_reg = OXYGEN_GPI_DATA; + data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; + data->generic.ext_power_bit = GPI_EXT_POWER; + xonar_init_ext_power(chip); + xonar_st_init(chip); +} + +static void xonar_d2_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + +static void xonar_hdav_cleanup(struct oxygen *chip) +{ + xonar_hdmi_cleanup(chip); + xonar_disable_output(chip); + msleep(2); +} + +static void xonar_st_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + +static void xonar_d2_suspend(struct oxygen *chip) +{ + xonar_d2_cleanup(chip); +} + +static void xonar_hdav_suspend(struct oxygen *chip) +{ + xonar_hdav_cleanup(chip); +} + +static void xonar_st_suspend(struct oxygen *chip) +{ + xonar_st_cleanup(chip); +} + +static void xonar_d2_resume(struct oxygen *chip) +{ + pcm1796_init(chip); + xonar_enable_output(chip); +} + +static void xonar_hdav_resume(struct oxygen *chip) +{ + struct xonar_hdav *data = chip->model_data; + + pcm1796_init(chip); + xonar_hdmi_resume(chip, &data->hdmi); + xonar_enable_output(chip); +} + +static void xonar_st_resume(struct oxygen *chip) +{ + pcm1796_init(chip); + xonar_enable_output(chip); +} + +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + data->oversampling = + params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 20, data->oversampling); +} + +static void set_hdav_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_hdav *data = chip->model_data; + + set_pcm1796_params(chip, params); + xonar_set_hdmi_params(chip, &data->hdmi, params); +} + +static const struct snd_kcontrol_new alt_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Loopback Switch", + .info = snd_ctl_boolean_mono_info, + .get = xonar_gpio_bit_switch_get, + .put = xonar_gpio_bit_switch_put, + .private_value = GPIO_D2_ALT, +}; + +static int st_output_switch_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Speakers", "Headphones", "FP Headphones" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item >= 3) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int st_output_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio; + + gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (!(gpio & GPIO_ST_HP)) + value->value.enumerated.item[0] = 0; + else if (gpio & GPIO_ST_HP_REAR) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + return 0; +} + + +static int st_output_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 gpio_old, gpio; + + mutex_lock(&chip->mutex); + gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA); + gpio = gpio_old; + switch (value->value.enumerated.item[0]) { + case 0: + gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR); + break; + case 1: + gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR; + break; + case 2: + gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR; + break; + } + oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + mutex_unlock(&chip->mutex); + return gpio != gpio_old; +} + +static const struct snd_kcontrol_new st_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = st_output_switch_get, + .put = st_output_switch_put, +}; + +static void xonar_line_mic_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_INPUT_ROUTE : 0, + GPIO_INPUT_ROUTE); + spin_unlock_irq(&chip->reg_lock); + } +} + +static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0); + +static int xonar_d2_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + /* CD in is actually connected to the video in pin */ + template->private_value ^= AC97_CD ^ AC97_VIDEO; + return 0; +} + +static int xonar_st_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + if (!strcmp(template->name, "Stereo Upmixing")) + return 1; /* stereo only - we don't need upmixing */ + return 0; +} + +static int xonar_d2_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); +} + +static int xonar_st_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); +} + +static const struct oxygen_model model_xonar_d2 = { + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_d2_init, + .control_filter = xonar_d2_control_filter, + .mixer_init = xonar_d2_mixer_init, + .cleanup = xonar_d2_cleanup, + .suspend = xonar_d2_suspend, + .resume = xonar_d2_resume, + .set_dac_params = set_pcm1796_params, + .set_adc_params = xonar_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_pcm179x), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF | + MIDI_OUTPUT | + MIDI_INPUT, + .dac_channels = 8, + .dac_volume_min = 255 - 2*60, + .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, +}; + +static const struct oxygen_model model_xonar_hdav = { + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_hdav_init, + .cleanup = xonar_hdav_cleanup, + .suspend = xonar_hdav_suspend, + .resume = xonar_hdav_resume, + .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, + .set_dac_params = set_hdav_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .uart_input = xonar_hdmi_uart_input, + .ac97_switch = xonar_line_mic_ac97_switch, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_hdav), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2 | + CAPTURE_1_FROM_SPDIF, + .dac_channels = 8, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .misc_flags = OXYGEN_MISC_MIDI, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static const struct oxygen_model model_xonar_st = { + .longname = "Asus Virtuoso 100", + .chip = "AV200", + .init = xonar_st_init, + .control_filter = xonar_st_control_filter, + .mixer_init = xonar_st_mixer_init, + .cleanup = xonar_st_cleanup, + .suspend = xonar_st_suspend, + .resume = xonar_st_resume, + .set_dac_params = set_pcm1796_params, + .set_adc_params = xonar_set_cs53x1_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .ac97_switch = xonar_line_mic_ac97_switch, + .dac_tlv = pcm1796_db_scale, + .model_data_size = sizeof(struct xonar_pcm179x), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_2, + .dac_channels = 2, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_2WIRE, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int __devinit get_xonar_pcm179x_model(struct oxygen *chip, + const struct pci_device_id *id) +{ + switch (id->subdevice) { + case 0x8269: + chip->model = model_xonar_d2; + chip->model.shortname = "Xonar D2"; + break; + case 0x82b7: + chip->model = model_xonar_d2; + chip->model.shortname = "Xonar D2X"; + chip->model.init = xonar_d2x_init; + break; + case 0x8314: + chip->model = model_xonar_hdav; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + default: + chip->model.shortname = "Xonar HDAV1.3"; + break; + case GPIO_DB_H6: + chip->model.shortname = "Xonar HDAV1.3+H6"; + chip->model.private_data = 1; + break; + } + break; + case 0x835d: + chip->model = model_xonar_st; + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK); + switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) { + default: + chip->model.shortname = "Xonar ST"; + break; + case GPIO_DB_H6: + chip->model.shortname = "Xonar ST+H6"; + chip->model.dac_channels = 8; + chip->model.private_data = 1; + break; + } + break; + case 0x835c: + chip->model = model_xonar_st; + chip->model.shortname = "Xonar STX"; + chip->model.init = xonar_stx_init; + break; + default: + return -EINVAL; + } + return 0; +} -- cgit v1.2.2 From 268304f4c4f0b8677d67400f04ad4e0271ec3742 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:15:01 +0200 Subject: sound: virtuoso: fix Xonar Essence ST support The Essence ST uses the CS2000 chip to generate the DAC master clock, so we better initialize and program it appropriately. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/cs2000.h | 83 ++++++++++++++++++++++++++++ sound/pci/oxygen/xonar_pcm179x.c | 113 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 sound/pci/oxygen/cs2000.h (limited to 'sound/pci') diff --git a/sound/pci/oxygen/cs2000.h b/sound/pci/oxygen/cs2000.h new file mode 100644 index 000000000000..c3501bdb5edc --- /dev/null +++ b/sound/pci/oxygen/cs2000.h @@ -0,0 +1,83 @@ +#ifndef CS2000_H_INCLUDED +#define CS2000_H_INCLUDED + +#define CS2000_DEV_ID 0x01 +#define CS2000_DEV_CTRL 0x02 +#define CS2000_DEV_CFG_1 0x03 +#define CS2000_DEV_CFG_2 0x04 +#define CS2000_GLOBAL_CFG 0x05 +#define CS2000_RATIO_0 0x06 /* 32 bits, big endian */ +#define CS2000_RATIO_1 0x0a +#define CS2000_RATIO_2 0x0e +#define CS2000_RATIO_3 0x12 +#define CS2000_FUN_CFG_1 0x16 +#define CS2000_FUN_CFG_2 0x17 +#define CS2000_FUN_CFG_3 0x1e + +/* DEV_ID */ +#define CS2000_DEVICE_MASK 0xf8 +#define CS2000_REVISION_MASK 0x07 + +/* DEV_CTRL */ +#define CS2000_UNLOCK 0x80 +#define CS2000_AUX_OUT_DIS 0x02 +#define CS2000_CLK_OUT_DIS 0x01 + +/* DEV_CFG_1 */ +#define CS2000_R_MOD_SEL_MASK 0xe0 +#define CS2000_R_MOD_SEL_1 0x00 +#define CS2000_R_MOD_SEL_2 0x20 +#define CS2000_R_MOD_SEL_4 0x40 +#define CS2000_R_MOD_SEL_8 0x60 +#define CS2000_R_MOD_SEL_1_2 0x80 +#define CS2000_R_MOD_SEL_1_4 0xa0 +#define CS2000_R_MOD_SEL_1_8 0xc0 +#define CS2000_R_MOD_SEL_1_16 0xe0 +#define CS2000_R_SEL_MASK 0x18 +#define CS2000_R_SEL_SHIFT 3 +#define CS2000_AUX_OUT_SRC_MASK 0x06 +#define CS2000_AUX_OUT_SRC_REF_CLK 0x00 +#define CS2000_AUX_OUT_SRC_CLK_IN 0x02 +#define CS2000_AUX_OUT_SRC_CLK_OUT 0x04 +#define CS2000_AUX_OUT_SRC_PLL_LOCK 0x06 +#define CS2000_EN_DEV_CFG_1 0x01 + +/* DEV_CFG_2 */ +#define CS2000_LOCK_CLK_MASK 0x06 +#define CS2000_LOCK_CLK_SHIFT 1 +#define CS2000_FRAC_N_SRC_MASK 0x01 +#define CS2000_FRAC_N_SRC_STATIC 0x00 +#define CS2000_FRAC_N_SRC_DYNAMIC 0x01 + +/* GLOBAL_CFG */ +#define CS2000_FREEZE 0x08 +#define CS2000_EN_DEV_CFG_2 0x01 + +/* FUN_CFG_1 */ +#define CS2000_CLK_SKIP_EN 0x80 +#define CS2000_AUX_LOCK_CFG_MASK 0x40 +#define CS2000_AUX_LOCK_CFG_PP_HIGH 0x00 +#define CS2000_AUX_LOCK_CFG_OD_LOW 0x40 +#define CS2000_REF_CLK_DIV_MASK 0x18 +#define CS2000_REF_CLK_DIV_4 0x00 +#define CS2000_REF_CLK_DIV_2 0x08 +#define CS2000_REF_CLK_DIV_1 0x10 + +/* FUN_CFG_2 */ +#define CS2000_CLK_OUT_UNL 0x10 +#define CS2000_L_F_RATIO_CFG_MASK 0x08 +#define CS2000_L_F_RATIO_CFG_20_12 0x00 +#define CS2000_L_F_RATIO_CFG_12_20 0x08 + +/* FUN_CFG_3 */ +#define CS2000_CLK_IN_BW_MASK 0x70 +#define CS2000_CLK_IN_BW_1 0x00 +#define CS2000_CLK_IN_BW_2 0x10 +#define CS2000_CLK_IN_BW_4 0x20 +#define CS2000_CLK_IN_BW_8 0x30 +#define CS2000_CLK_IN_BW_16 0x40 +#define CS2000_CLK_IN_BW_32 0x50 +#define CS2000_CLK_IN_BW_64 0x60 +#define CS2000_CLK_IN_BW_128 0x70 + +#endif diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index eb5f015fcd23..522efde0d52e 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -91,6 +91,9 @@ * CMI8788: * * I²C <-> PCM1792A + * <-> CS2000 (ST only) + * + * ADC1 MCLK -> REF_CLK of CS2000 (ST only) * * GPI 0 <- external power present (STX only) * @@ -124,6 +127,7 @@ #include "xonar.h" #include "cm9780.h" #include "pcm1796.h" +#include "cs2000.h" #define GPIO_D2X_EXT_POWER 0x0020 @@ -143,12 +147,14 @@ #define GPIO_ST_HP 0x0080 #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ +#define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 oversampling; + u8 cs2000_fun_cfg_1; }; struct xonar_hdav { @@ -188,6 +194,11 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec, pcm1796_write_i2c(chip, codec, reg, value); } +static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); +} + static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; @@ -292,14 +303,17 @@ static void xonar_hdav_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } -static void xonar_st_init(struct oxygen *chip) +static void xonar_st_init_i2c(struct oxygen *chip) { - struct xonar_pcm179x *data = chip->model_data; - oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_SPEED_FAST); +} + +static void xonar_st_init_common(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; @@ -320,15 +334,57 @@ static void xonar_st_init(struct oxygen *chip) snd_component_add(chip->card, "CS5381"); } +static void cs2000_registers_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_FREEZE); + cs2000_write(chip, CS2000_DEV_CTRL, 0); + cs2000_write(chip, CS2000_DEV_CFG_1, + CS2000_R_MOD_SEL_1 | + (0 << CS2000_R_SEL_SHIFT) | + CS2000_AUX_OUT_SRC_REF_CLK | + CS2000_EN_DEV_CFG_1); + cs2000_write(chip, CS2000_DEV_CFG_2, + (0 << CS2000_LOCK_CLK_SHIFT) | + CS2000_FRAC_N_SRC_STATIC); + cs2000_write(chip, CS2000_RATIO_0 + 0, 0x00); /* 1.0 */ + cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10); + cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00); + cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00); + cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); + cs2000_write(chip, CS2000_FUN_CFG_2, 0); + cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2); +} + +static void xonar_st_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + + xonar_st_init_i2c(chip); + cs2000_registers_init(chip); + xonar_st_init_common(chip); + + snd_component_add(chip->card, "CS2000"); +} + static void xonar_stx_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + xonar_st_init_i2c(chip); data->generic.ext_power_reg = OXYGEN_GPI_DATA; data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->generic.ext_power_bit = GPI_EXT_POWER; xonar_init_ext_power(chip); - xonar_st_init(chip); + xonar_st_init_common(chip); } static void xonar_d2_cleanup(struct oxygen *chip) @@ -378,12 +434,18 @@ static void xonar_hdav_resume(struct oxygen *chip) xonar_enable_output(chip); } -static void xonar_st_resume(struct oxygen *chip) +static void xonar_stx_resume(struct oxygen *chip) { pcm1796_init(chip); xonar_enable_output(chip); } +static void xonar_st_resume(struct oxygen *chip) +{ + cs2000_registers_init(chip); + xonar_stx_resume(chip); +} + static void set_pcm1796_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { @@ -396,6 +458,43 @@ static void set_pcm1796_params(struct oxygen *chip, pcm1796_write(chip, i, 20, data->oversampling); } +static void set_cs2000_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + /* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */ + static const u8 rate_mclks[] = { + [OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128, + [OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, + [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, + }; + struct xonar_pcm179x *data = chip->model_data; + unsigned int rate_index; + u8 rate_mclk; + + rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) + & OXYGEN_I2S_RATE_MASK; + rate_mclk = rate_mclks[rate_index]; + oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, + OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); + if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + else + data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_2; + cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); +} + +static void set_st_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + set_cs2000_params(chip, params); + set_pcm1796_params(chip, params); +} + static void set_hdav_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { @@ -590,7 +689,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, - .set_dac_params = set_pcm1796_params, + .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, .update_dac_mute = update_pcm1796_mute, @@ -652,6 +751,8 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model = model_xonar_st; chip->model.shortname = "Xonar STX"; chip->model.init = xonar_stx_init; + chip->model.resume = xonar_stx_resume; + chip->model.set_dac_params = set_pcm1796_params; break; default: return -EINVAL; -- cgit v1.2.2 From 75919d7c057be888c7cd7b192fad02182260b04a Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:15:49 +0200 Subject: sound: oxygen: better defaults for upmixing control On card models with two-channel outputs, the base driver can automatically disable the upmixing control so that the model drivers do not need to do this. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/hifier.c | 8 -------- sound/pci/oxygen/oxygen_mixer.c | 3 +++ sound/pci/oxygen/xonar_pcm179x.c | 2 -- 3 files changed, 3 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 84ef13183419..9026a143a5ec 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -141,19 +141,11 @@ static void set_cs5340_params(struct oxygen *chip, 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, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ - return 0; -} - static const struct oxygen_model model_hifier = { .shortname = "C-Media CMI8787", .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = hifier_init, - .control_filter = hifier_control_filter, .cleanup = hifier_cleanup, .resume = hifier_resume, .set_dac_params = set_ak4396_params, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 5401c547c4e3..e8e911a86c8e 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -954,6 +954,9 @@ static int add_controls(struct oxygen *chip, if (err == 1) continue; } + if (!strcmp(template.name, "Stereo Upmixing") && + chip->model.dac_channels == 2) + continue; if (!strcmp(template.name, "Master Playback Volume") && chip->model.dac_tlv) { template.tlv.p = chip->model.dac_tlv; diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 522efde0d52e..07aaa893d323 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -605,8 +605,6 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) { if (!strncmp(template->name, "CD Capture ", 11)) return 1; /* no CD input */ - if (!strcmp(template->name, "Stereo Upmixing")) - return 1; /* stereo only - we don't need upmixing */ return 0; } -- cgit v1.2.2 From 3d8bb454c4fbe18cea1adfd4183a4a9ef5f0ef04 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:16:41 +0200 Subject: sound: oxygen: add stereo upmixing to center/LFE channels Add the possibility to route a mix of the two channels of stereo data to the center and LFE outputs. This is implemented only for models where the DACs support this, i.e., for the Xonar D1 and DX. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen.h | 1 + sound/pci/oxygen/oxygen_mixer.c | 33 ++++++++++++++++++++++++--------- sound/pci/oxygen/oxygen_pcm.c | 6 ++++-- sound/pci/oxygen/xonar_cs43xx.c | 39 +++++++++++++++++++++++++++++---------- 4 files changed, 58 insertions(+), 21 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index bd615dbffadb..2ac3b3c8253f 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -84,6 +84,7 @@ struct oxygen_model { struct snd_pcm_hw_params *params); void (*update_dac_volume)(struct oxygen *chip); void (*update_dac_mute)(struct oxygen *chip); + void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed); void (*gpio_changed)(struct oxygen *chip); void (*uart_input)(struct oxygen *chip); void (*ac97_switch)(struct oxygen *chip, diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index e8e911a86c8e..5dfb5fb73381 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -99,11 +99,15 @@ static int dac_mute_put(struct snd_kcontrol *ctl, static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { - static const char *const names[3] = { - "Front", "Front+Surround", "Front+Surround+Back" + static const char *const names[5] = { + "Front", + "Front+Surround", + "Front+Surround+Back", + "Front+Surround+Center/LFE", + "Front+Surround+Center/LFE+Back", }; struct oxygen *chip = ctl->private_data; - unsigned int count = 2 + (chip->model.dac_channels == 8); + unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; info->count = 1; @@ -127,7 +131,7 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) void oxygen_update_dac_routing(struct oxygen *chip) { /* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */ - static const unsigned int reg_values[3] = { + static const unsigned int reg_values[5] = { /* stereo -> front */ (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | @@ -143,6 +147,16 @@ void oxygen_update_dac_routing(struct oxygen *chip) (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+center/LFE */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+center/LFE+back */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), }; u8 channels; unsigned int reg_value; @@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip) OXYGEN_PLAY_DAC1_SOURCE_MASK | OXYGEN_PLAY_DAC2_SOURCE_MASK | OXYGEN_PLAY_DAC3_SOURCE_MASK); + if (chip->model.update_center_lfe_mix) + chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2); } static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; - unsigned int count = 2 + (chip->model.dac_channels == 8); + unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3; int changed; + if (value->value.enumerated.item[0] >= count) + return -EINVAL; mutex_lock(&chip->mutex); changed = value->value.enumerated.item[0] != chip->dac_routing; if (changed) { - chip->dac_routing = min(value->value.enumerated.item[0], - count - 1); - spin_lock_irq(&chip->reg_lock); + chip->dac_routing = value->value.enumerated.item[0]; oxygen_update_dac_routing(chip); - spin_unlock_irq(&chip->reg_lock); } mutex_unlock(&chip->mutex); return changed; diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index ef2345d82b86..1e98333366df 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -435,6 +435,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + mutex_lock(&chip->mutex); spin_lock_irq(&chip->reg_lock); oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, OXYGEN_SPDIF_OUT_ENABLE); @@ -446,6 +447,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, OXYGEN_SPDIF_OUT_RATE_MASK); oxygen_update_spdif_source(chip); spin_unlock_irq(&chip->reg_lock); + mutex_unlock(&chip->mutex); return 0; } @@ -459,6 +461,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; + mutex_lock(&chip->mutex); spin_lock_irq(&chip->reg_lock); oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, oxygen_play_channels(hw_params), @@ -475,12 +478,11 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, OXYGEN_I2S_FORMAT_MASK | OXYGEN_I2S_MCLK_MASK | OXYGEN_I2S_BITS_MASK); - oxygen_update_dac_routing(chip); oxygen_update_spdif_source(chip); spin_unlock_irq(&chip->reg_lock); - mutex_lock(&chip->mutex); chip->model.set_dac_params(chip, hw_params); + oxygen_update_dac_routing(chip); mutex_unlock(&chip->mutex); return 0; } diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 8fb5797577dd..0fa05ed6681d 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -67,6 +67,7 @@ struct xonar_cs43xx { struct xonar_generic generic; u8 cs4398_fm; u8 cs4362a_fm; + u8 cs4362a_fm_c; }; static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) @@ -128,7 +129,7 @@ static void cs43xx_init(struct oxygen *chip) cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE); cs4362a_write(chip, 0x05, 0); cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); cs4362a_write(chip, 0x0c, data->cs4362a_fm); update_cs43xx_volume(chip); update_cs43xx_mute(chip); @@ -146,6 +147,7 @@ static void xonar_d1_init(struct oxygen *chip) data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; data->cs4362a_fm = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + data->cs4362a_fm_c = data->cs4362a_fm; oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | @@ -202,25 +204,41 @@ static void set_cs43xx_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { struct xonar_cs43xx *data = chip->model_data; + u8 cs4398_fm, cs4362a_fm; - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; if (params_rate(params) <= 50000) { - data->cs4398_fm |= CS4398_FM_SINGLE; - data->cs4362a_fm |= CS4362A_FM_SINGLE; + cs4398_fm = CS4398_FM_SINGLE; + cs4362a_fm = CS4362A_FM_SINGLE; } else if (params_rate(params) <= 100000) { - data->cs4398_fm |= CS4398_FM_DOUBLE; - data->cs4362a_fm |= CS4362A_FM_DOUBLE; + cs4398_fm = CS4398_FM_DOUBLE; + cs4362a_fm = CS4362A_FM_DOUBLE; } else { - data->cs4398_fm |= CS4398_FM_QUAD; - data->cs4362a_fm |= CS4362A_FM_QUAD; + cs4398_fm = CS4398_FM_QUAD; + cs4362a_fm = CS4362A_FM_QUAD; } + data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST | cs4398_fm; + data->cs4362a_fm = + (data->cs4362a_fm & ~CS4362A_FM_MASK) | cs4362a_fm; + data->cs4362a_fm_c = + (data->cs4362a_fm_c & ~CS4362A_FM_MASK) | cs4362a_fm; cs4398_write(chip, 2, data->cs4398_fm); cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm); + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); cs4362a_write(chip, 0x0c, data->cs4362a_fm); } +static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed) +{ + struct xonar_cs43xx *data = chip->model_data; + + data->cs4362a_fm_c &= ~CS4362A_ATAPI_MASK; + if (mixed) + data->cs4362a_fm_c |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; + else + data->cs4362a_fm_c |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + cs4362a_write(chip, 0x09, data->cs4362a_fm_c); +} + static const struct snd_kcontrol_new front_panel_switch = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Front Panel Switch", @@ -269,6 +287,7 @@ static const struct oxygen_model model_xonar_d1 = { .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_cs43xx_volume, .update_dac_mute = update_cs43xx_mute, + .update_center_lfe_mix = update_cs43xx_center_lfe_mix, .ac97_switch = xonar_d1_line_mic_ac97_switch, .dac_tlv = cs4362a_db_scale, .model_data_size = sizeof(struct xonar_cs43xx), -- cgit v1.2.2 From dc0adf48daa81b05765d3c5ebab76321f77e9d21 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:17:36 +0200 Subject: sound: oxygen: more hardware documentation Add some comments describing the hardware pin routing. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/hifier.c | 6 ++++++ sound/pci/oxygen/oxygen.c | 6 ++++++ sound/pci/oxygen/xonar_cs43xx.c | 4 ++++ sound/pci/oxygen/xonar_pcm179x.c | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 9026a143a5ec..19e9e0123304 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -17,6 +17,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + * CMI8788: + * + * SPI 0 -> AK4396 + */ + #include #include #include diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 72db4c39007f..53dff7193f31 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -18,6 +18,8 @@ */ /* + * CMI8788: + * * SPI 0 -> 1st AK4396 (front) * SPI 1 -> 2nd AK4396 (surround) * SPI 2 -> 3rd AK4396 (center/LFE) @@ -27,6 +29,10 @@ * GPIO 0 -> DFS0 of AK5385 * GPIO 1 -> DFS1 of AK5385 * GPIO 8 -> enable headphone amplifier on HT-Omega models + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input */ #include diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 0fa05ed6681d..a8ec4e8271a4 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -41,6 +41,10 @@ * CS4362A: * * AD0 <- 0 + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input */ #include diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 07aaa893d323..97574dbec2b6 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -32,6 +32,10 @@ * GPIO 5 <- external power present (D2X only) * GPIO 7 -> ALT * GPIO 8 -> enable output to speakers + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input */ /* @@ -54,6 +58,10 @@ * * PCM1796 front: AD1,0 <- 0,0 * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input + * * no daughterboard * ---------------- * @@ -107,6 +115,15 @@ * PCM1792A: * * AD1,0 <- 0,0 + * SCK <- CLK_OUT of CS2000 (ST only) + * + * CS2000: + * + * AD0 <- 0 + * + * CM9780: + * + * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input * * H6 daughterboard * ---------------- -- cgit v1.2.2 From 6f0de3ce068e48b033b5e4d0822b47218e9d206c Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:18:45 +0200 Subject: sound: oxygen: cache codec registers Keep a cache of codec registers to avoid unnecessary writes. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/hifier.c | 46 ++++++++----- sound/pci/oxygen/oxygen.c | 107 ++++++++++++++++-------------- sound/pci/oxygen/xonar_cs43xx.c | 140 ++++++++++++++++++++++++--------------- sound/pci/oxygen/xonar_pcm179x.c | 109 ++++++++++++++++++++---------- 4 files changed, 250 insertions(+), 152 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 19e9e0123304..2079c100aabc 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -57,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = { MODULE_DEVICE_TABLE(pci, hifier_ids); struct hifier_data { - u8 ak4396_ctl2; + u8 ak4396_regs[5]; }; static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) { + struct hifier_data *data = chip->model_data; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_CLOCK_160 | (0 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, AK4396_WRITE | (reg << 8) | value); + data->ak4396_regs[reg] = value; } -static void update_ak4396_volume(struct oxygen *chip) +static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value) { - ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); - ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); + struct hifier_data *data = chip->model_data; + + if (value != data->ak4396_regs[reg]) + ak4396_write(chip, reg, value); } static void hifier_registers_init(struct oxygen *chip) @@ -81,16 +86,19 @@ static void hifier_registers_init(struct oxygen *chip) struct hifier_data *data = chip->model_data; 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_2, + data->ak4396_regs[AK4396_CONTROL_2]); ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); - update_ak4396_volume(chip); + ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); } static void hifier_init(struct oxygen *chip) { struct hifier_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_regs[AK4396_CONTROL_2] = + AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; hifier_registers_init(chip); snd_component_add(chip->card, "AK4396"); @@ -112,20 +120,29 @@ static void set_ak4396_params(struct oxygen *chip, struct hifier_data *data = chip->model_data; u8 value; - value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK; if (params_rate(params) <= 54000) value |= AK4396_DFS_NORMAL; else if (params_rate(params) <= 108000) value |= AK4396_DFS_DOUBLE; else value |= AK4396_DFS_QUAD; - data->ak4396_ctl2 = value; msleep(1); /* wait for the new MCLK to become stable */ - ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB); - ak4396_write(chip, AK4396_CONTROL_2, value); - ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + if (value != data->ak4396_regs[AK4396_CONTROL_2]) { + ak4396_write(chip, AK4396_CONTROL_1, + AK4396_DIF_24_MSB); + ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write(chip, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]); } static void update_ak4396_mute(struct oxygen *chip) @@ -133,11 +150,10 @@ static void update_ak4396_mute(struct oxygen *chip) struct hifier_data *data = chip->model_data; u8 value; - value = data->ak4396_ctl2 & ~AK4396_SMUTE; + value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE; if (chip->dac_mute) value |= AK4396_SMUTE; - data->ak4396_ctl2 = value; - ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write_cached(chip, AK4396_CONTROL_2, value); } static void set_cs5340_params(struct oxygen *chip, diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 53dff7193f31..c986c5ebf65b 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -97,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); #define GPIO_CLARO_HP 0x0100 struct generic_data { - u8 ak4396_ctl2; - u16 saved_wm8785_registers[2]; + u8 ak4396_regs[4][5]; + u16 wm8785_regs[1]; }; static void ak4396_write(struct oxygen *chip, unsigned int codec, @@ -108,12 +108,24 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec, static const u8 codec_spi_map[4] = { 0, 1, 2, 4 }; + struct generic_data *data = chip->model_data; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | OXYGEN_SPI_DATA_LENGTH_2 | OXYGEN_SPI_CLOCK_160 | (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_HI, AK4396_WRITE | (reg << 8) | value); + data->ak4396_regs[codec][reg] = value; +} + +static void ak4396_write_cached(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + struct generic_data *data = chip->model_data; + + if (value != data->ak4396_regs[codec][reg]) + ak4396_write(chip, codec, reg, value); } static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) @@ -126,20 +138,8 @@ static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) (3 << OXYGEN_SPI_CODEC_SHIFT) | OXYGEN_SPI_CEN_LATCH_CLOCK_LO, (reg << 9) | value); - if (reg < ARRAY_SIZE(data->saved_wm8785_registers)) - data->saved_wm8785_registers[reg] = value; -} - -static void update_ak4396_volume(struct oxygen *chip) -{ - unsigned int i; - - for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_LCH_ATT, chip->dac_volume[i * 2]); - ak4396_write(chip, i, - AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]); - } + if (reg < ARRAY_SIZE(data->wm8785_regs)) + data->wm8785_regs[reg] = value; } static void ak4396_registers_init(struct oxygen *chip) @@ -148,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip) unsigned int i; for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); - ak4396_write(chip, i, - AK4396_CONTROL_2, data->ak4396_ctl2); - ak4396_write(chip, i, - AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, i, AK4396_CONTROL_2, + data->ak4396_regs[0][AK4396_CONTROL_2]); + ak4396_write(chip, i, AK4396_CONTROL_3, + AK4396_PCM); + ak4396_write(chip, i, AK4396_LCH_ATT, + chip->dac_volume[i * 2]); + ak4396_write(chip, i, AK4396_RCH_ATT, + chip->dac_volume[i * 2 + 1]); } - update_ak4396_volume(chip); } static void ak4396_init(struct oxygen *chip) { struct generic_data *data = chip->model_data; - data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; + data->ak4396_regs[0][AK4396_CONTROL_2] = + AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL; ak4396_registers_init(chip); snd_component_add(chip->card, "AK4396"); } @@ -179,17 +183,15 @@ static void wm8785_registers_init(struct oxygen *chip) struct generic_data *data = chip->model_data; wm8785_write(chip, WM8785_R7, 0); - wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]); - wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]); + wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]); } static void wm8785_init(struct oxygen *chip) { struct generic_data *data = chip->model_data; - data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE | - WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; - data->saved_wm8785_registers[1] = WM8785_WL_24; + data->wm8785_regs[0] = + WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; wm8785_registers_init(chip); snd_component_add(chip->card, "WM8785"); } @@ -270,24 +272,36 @@ static void set_ak4396_params(struct oxygen *chip, unsigned int i; u8 value; - value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK; if (params_rate(params) <= 54000) value |= AK4396_DFS_NORMAL; else if (params_rate(params) <= 108000) value |= AK4396_DFS_DOUBLE; else value |= AK4396_DFS_QUAD; - data->ak4396_ctl2 = value; msleep(1); /* wait for the new MCLK to become stable */ + if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) { + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB); + ak4396_write(chip, i, AK4396_CONTROL_2, value); + ak4396_write(chip, i, AK4396_CONTROL_1, + AK4396_DIF_24_MSB | AK4396_RSTN); + } + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + unsigned int i; + for (i = 0; i < 4; ++i) { - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB); - ak4396_write(chip, i, - AK4396_CONTROL_2, value); - ak4396_write(chip, i, - AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write_cached(chip, i, AK4396_LCH_ATT, + chip->dac_volume[i * 2]); + ak4396_write_cached(chip, i, AK4396_RCH_ATT, + chip->dac_volume[i * 2 + 1]); } } @@ -297,21 +311,19 @@ static void update_ak4396_mute(struct oxygen *chip) unsigned int i; u8 value; - value = data->ak4396_ctl2 & ~AK4396_SMUTE; + value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE; if (chip->dac_mute) value |= AK4396_SMUTE; - data->ak4396_ctl2 = value; for (i = 0; i < 4; ++i) - ak4396_write(chip, i, AK4396_CONTROL_2, value); + ak4396_write_cached(chip, i, AK4396_CONTROL_2, value); } static void set_wm8785_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { + struct generic_data *data = chip->model_data; unsigned int value; - wm8785_write(chip, WM8785_R7, 0); - value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; if (params_rate(params) <= 48000) value |= WM8785_OSR_SINGLE; @@ -319,13 +331,10 @@ static void set_wm8785_params(struct oxygen *chip, value |= WM8785_OSR_DOUBLE; else value |= WM8785_OSR_QUAD; - wm8785_write(chip, WM8785_R0, value); - - if (snd_pcm_format_width(params_format(params)) <= 16) - value = WM8785_WL_16; - else - value = WM8785_WL_24; - wm8785_write(chip, WM8785_R1, value); + if (value != data->wm8785_regs[0]) { + wm8785_write(chip, WM8785_R7, 0); + wm8785_write(chip, WM8785_R0, value); + } } static void set_ak5385_params(struct oxygen *chip, diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index a8ec4e8271a4..330c5e755917 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -69,62 +69,58 @@ struct xonar_cs43xx { struct xonar_generic generic; - u8 cs4398_fm; - u8 cs4362a_fm; - u8 cs4362a_fm_c; + u8 cs4398_regs[7]; + u8 cs4362a_regs[15]; }; static void cs4398_write(struct oxygen *chip, u8 reg, u8 value) { - oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); -} + struct xonar_cs43xx *data = chip->model_data; -static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) -{ - oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); + oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); + if (reg < ARRAY_SIZE(data->cs4398_regs)) + data->cs4398_regs[reg] = value; } -static void update_cs4362a_volumes(struct oxygen *chip) +static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value) { - u8 mute; + struct xonar_cs43xx *data = chip->model_data; - 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); + if (value != data->cs4398_regs[reg]) + cs4398_write(chip, reg, value); } -static void update_cs43xx_volume(struct oxygen *chip) +static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value) { - cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2); - cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2); - update_cs4362a_volumes(chip); + struct xonar_cs43xx *data = chip->model_data; + + oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value); + if (reg < ARRAY_SIZE(data->cs4362a_regs)) + data->cs4362a_regs[reg] = value; } -static void update_cs43xx_mute(struct oxygen *chip) +static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value) { - u8 reg; + struct xonar_cs43xx *data = chip->model_data; - 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); + if (value != data->cs4362a_regs[reg]) + cs4362a_write(chip, reg, value); } -static void cs43xx_init(struct oxygen *chip) +static void cs43xx_registers_init(struct oxygen *chip) { struct xonar_cs43xx *data = chip->model_data; + unsigned int i; /* 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, data->cs4398_fm); + cs4398_write(chip, 2, data->cs4398_regs[2]); cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L); + cs4398_write(chip, 4, data->cs4398_regs[4]); + cs4398_write(chip, 5, data->cs4398_regs[5]); + cs4398_write(chip, 6, data->cs4398_regs[6]); cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST); @@ -132,11 +128,8 @@ static void cs43xx_init(struct oxygen *chip) 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, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); - update_cs43xx_volume(chip); - update_cs43xx_mute(chip); + for (i = 6; i <= 14; ++i) + cs4362a_write(chip, i, data->cs4362a_regs[i]); /* clear power down */ cs4398_write(chip, 8, CS4398_CPEN); cs4362a_write(chip, 0x01, CS4362A_CPEN); @@ -148,17 +141,29 @@ static void xonar_d1_init(struct oxygen *chip) data->generic.anti_pop_delay = 800; data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE; - data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; - data->cs4362a_fm = CS4362A_FM_SINGLE | + data->cs4398_regs[2] = + CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST; + data->cs4398_regs[4] = CS4398_MUTEP_LOW | + CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE; + data->cs4398_regs[5] = 60 * 2; + data->cs4398_regs[6] = 60 * 2; + data->cs4362a_regs[6] = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - data->cs4362a_fm_c = data->cs4362a_fm; + data->cs4362a_regs[7] = 60 | CS4362A_MUTE; + data->cs4362a_regs[8] = 60 | CS4362A_MUTE; + data->cs4362a_regs[9] = data->cs4362a_regs[6]; + data->cs4362a_regs[10] = 60 | CS4362A_MUTE; + data->cs4362a_regs[11] = 60 | CS4362A_MUTE; + data->cs4362a_regs[12] = data->cs4362a_regs[6]; + data->cs4362a_regs[13] = 60 | CS4362A_MUTE; + data->cs4362a_regs[14] = 60 | CS4362A_MUTE; oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, OXYGEN_2WIRE_LENGTH_8 | OXYGEN_2WIRE_INTERRUPT_MASK | OXYGEN_2WIRE_SPEED_FAST); - cs43xx_init(chip); + cs43xx_registers_init(chip); oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); @@ -200,7 +205,7 @@ static void xonar_d1_resume(struct oxygen *chip) { oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC); msleep(1); - cs43xx_init(chip); + cs43xx_registers_init(chip); xonar_enable_output(chip); } @@ -220,27 +225,56 @@ static void set_cs43xx_params(struct oxygen *chip, cs4398_fm = CS4398_FM_QUAD; cs4362a_fm = CS4362A_FM_QUAD; } - data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST | cs4398_fm; - data->cs4362a_fm = - (data->cs4362a_fm & ~CS4362A_FM_MASK) | cs4362a_fm; - data->cs4362a_fm_c = - (data->cs4362a_fm_c & ~CS4362A_FM_MASK) | cs4362a_fm; - cs4398_write(chip, 2, data->cs4398_fm); - cs4362a_write(chip, 0x06, data->cs4362a_fm); - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); - cs4362a_write(chip, 0x0c, data->cs4362a_fm); + cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST; + cs4398_write_cached(chip, 2, cs4398_fm); + cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK; + cs4362a_write_cached(chip, 6, cs4362a_fm); + cs4362a_write_cached(chip, 12, cs4362a_fm); + cs4362a_fm &= CS4362A_FM_MASK; + cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK; + cs4362a_write_cached(chip, 9, cs4362a_fm); +} + +static void update_cs4362a_volumes(struct oxygen *chip) +{ + unsigned int i; + u8 mute; + + mute = chip->dac_mute ? CS4362A_MUTE : 0; + for (i = 0; i < 6; ++i) + cs4362a_write_cached(chip, 7 + i + i / 2, + (127 - chip->dac_volume[2 + i]) | mute); +} + +static void update_cs43xx_volume(struct oxygen *chip) +{ + cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2); + cs4398_write_cached(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_cached(chip, 4, reg); + update_cs4362a_volumes(chip); } static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed) { struct xonar_cs43xx *data = chip->model_data; + u8 reg; - data->cs4362a_fm_c &= ~CS4362A_ATAPI_MASK; + reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK; if (mixed) - data->cs4362a_fm_c |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; + reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR; else - data->cs4362a_fm_c |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; - cs4362a_write(chip, 0x09, data->cs4362a_fm_c); + reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; + cs4362a_write_cached(chip, 9, reg); } static const struct snd_kcontrol_new front_panel_switch = { diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 97574dbec2b6..e17ee5e8e510 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -166,11 +166,13 @@ #define I2C_DEVICE_PCM1796(i) (0x98 + ((i) << 1)) /* 10011, ii, /W=0 */ #define I2C_DEVICE_CS2000 0x9c /* 100111, 0, /W=0 */ +#define PCM1796_REG_BASE 16 + struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; - u8 oversampling; + u8 pcm1796_regs[4][5]; u8 cs2000_fun_cfg_1; }; @@ -204,54 +206,71 @@ static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, static void pcm1796_write(struct oxygen *chip, unsigned int codec, u8 reg, u8 value) { + struct xonar_pcm179x *data = chip->model_data; + if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == OXYGEN_FUNCTION_SPI) pcm1796_write_spi(chip, codec, reg, value); else pcm1796_write_i2c(chip, codec, reg, value); + if ((unsigned int)(reg - PCM1796_REG_BASE) + < ARRAY_SIZE(data->pcm1796_regs[codec])) + data->pcm1796_regs[codec][reg - PCM1796_REG_BASE] = value; } -static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) +static void pcm1796_write_cached(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) { - oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); + struct xonar_pcm179x *data = chip->model_data; + + if (value != data->pcm1796_regs[codec][reg - PCM1796_REG_BASE]) + pcm1796_write(chip, codec, reg, value); } -static void update_pcm1796_volume(struct oxygen *chip) +static void cs2000_write(struct oxygen *chip, u8 reg, u8 value) { struct xonar_pcm179x *data = chip->model_data; - unsigned int i; - for (i = 0; i < data->dacs; ++i) { - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); - } + oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value); + if (reg == CS2000_FUN_CFG_1) + data->cs2000_fun_cfg_1 = value; } -static void update_pcm1796_mute(struct oxygen *chip) +static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value) { struct xonar_pcm179x *data = chip->model_data; - unsigned int i; - u8 value; - value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; - if (chip->dac_mute) - value |= PCM1796_MUTE; - for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 18, value); + if (reg != CS2000_FUN_CFG_1 || + value != data->cs2000_fun_cfg_1) + cs2000_write(chip, reg, value); } -static void pcm1796_init(struct oxygen *chip) +static void pcm1796_registers_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; for (i = 0; i < data->dacs; ++i) { + /* set ATLD before ATL/ATR */ + pcm1796_write(chip, i, 18, + data->pcm1796_regs[0][18 - PCM1796_REG_BASE]); + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); - pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write(chip, i, 20, + data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 21, 0); } - update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */ - update_pcm1796_volume(chip); +} + +static void pcm1796_init(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | + PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; + pcm1796_registers_init(chip); } static void xonar_d2_init(struct oxygen *chip) @@ -261,7 +280,6 @@ static void xonar_d2_init(struct oxygen *chip) data->generic.anti_pop_delay = 300; data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE; data->dacs = 4; - data->oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -304,7 +322,6 @@ static void xonar_hdav_init(struct oxygen *chip) data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER; data->pcm179x.dacs = chip->model.private_data ? 4 : 1; - data->pcm179x.oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -335,7 +352,6 @@ static void xonar_st_init_common(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; - data->oversampling = PCM1796_OS_64; pcm1796_init(chip); @@ -438,7 +454,7 @@ static void xonar_st_suspend(struct oxygen *chip) static void xonar_d2_resume(struct oxygen *chip) { - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_enable_output(chip); } @@ -446,14 +462,14 @@ static void xonar_hdav_resume(struct oxygen *chip) { struct xonar_hdav *data = chip->model_data; - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_hdmi_resume(chip, &data->hdmi); xonar_enable_output(chip); } static void xonar_stx_resume(struct oxygen *chip) { - pcm1796_init(chip); + pcm1796_registers_init(chip); xonar_enable_output(chip); } @@ -468,11 +484,35 @@ static void set_pcm1796_params(struct oxygen *chip, { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + u8 reg; + + reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < data->dacs; ++i) + pcm1796_write_cached(chip, i, 20, reg); +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + + for (i = 0; i < data->dacs; ++i) { + pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + u8 value; - data->oversampling = - params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; for (i = 0; i < data->dacs; ++i) - pcm1796_write(chip, i, 20, data->oversampling); + pcm1796_write_cached(chip, i, 18, value); } static void set_cs2000_params(struct oxygen *chip, @@ -489,9 +529,8 @@ static void set_cs2000_params(struct oxygen *chip, [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, }; - struct xonar_pcm179x *data = chip->model_data; unsigned int rate_index; - u8 rate_mclk; + u8 rate_mclk, reg; rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) & OXYGEN_I2S_RATE_MASK; @@ -499,10 +538,10 @@ static void set_cs2000_params(struct oxygen *chip, oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) - data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; + reg = CS2000_REF_CLK_DIV_1; else - data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_2; - cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1); + reg = CS2000_REF_CLK_DIV_2; + cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg); } static void set_st_params(struct oxygen *chip, -- cgit v1.2.2 From a361e247b4e36c567b44fef354ab595458422d44 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:19:19 +0200 Subject: sound: virtuoso: add headphone impedance control Add a mixer control to adjust the headphone amplifier output for headphones with different impedances. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/xonar_pcm179x.c | 110 +++++++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index e17ee5e8e510..cf94e4432a3f 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -173,6 +173,8 @@ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 pcm1796_regs[4][5]; + bool hp_active; + s8 hp_gain_offset; u8 cs2000_fun_cfg_1; }; @@ -249,13 +251,17 @@ static void pcm1796_registers_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + s8 gain_offset; + gain_offset = data->hp_active ? data->hp_gain_offset : 0; for (i = 0; i < data->dacs; ++i) { /* set ATLD before ATL/ATR */ pcm1796_write(chip, i, 18, data->pcm1796_regs[0][18 - PCM1796_REG_BASE]); - pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2] + + gain_offset); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1] + + gain_offset); pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); pcm1796_write(chip, i, 20, data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); @@ -352,6 +358,7 @@ static void xonar_st_init_common(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE; data->dacs = chip->model.private_data ? 4 : 1; + data->hp_gain_offset = 2*-18; pcm1796_init(chip); @@ -495,10 +502,14 @@ static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; + s8 gain_offset; + gain_offset = data->hp_active ? data->hp_gain_offset : 0; for (i = 0; i < data->dacs; ++i) { - pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]); - pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]); + pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2] + + gain_offset); + pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1] + + gain_offset); } } @@ -606,6 +617,7 @@ static int st_output_switch_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) { struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; u16 gpio_old, gpio; mutex_lock(&chip->mutex); @@ -623,16 +635,83 @@ static int st_output_switch_put(struct snd_kcontrol *ctl, break; } oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio); + data->hp_active = gpio & GPIO_ST_HP; + update_pcm1796_volume(chip); mutex_unlock(&chip->mutex); return gpio != gpio_old; } -static const struct snd_kcontrol_new st_output_switch = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Output", - .info = st_output_switch_info, - .get = st_output_switch_get, - .put = st_output_switch_put, +static int st_hp_volume_offset_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "< 64 ohms", "64-300 ohms", "300-600 ohms" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item > 2) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int st_hp_volume_offset_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + mutex_lock(&chip->mutex); + if (data->hp_gain_offset < 2*-6) + value->value.enumerated.item[0] = 0; + else if (data->hp_gain_offset < 0) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + mutex_unlock(&chip->mutex); + return 0; +} + + +static int st_hp_volume_offset_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + static const s8 offsets[] = { 2*-18, 2*-6, 0 }; + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + s8 offset; + int changed; + + if (value->value.enumerated.item[0] > 2) + return -EINVAL; + offset = offsets[value->value.enumerated.item[0]]; + mutex_lock(&chip->mutex); + changed = offset != data->hp_gain_offset; + if (changed) { + data->hp_gain_offset = offset; + update_pcm1796_volume(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new st_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Output", + .info = st_output_switch_info, + .get = st_output_switch_get, + .put = st_output_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphones Impedance Playback Enum", + .info = st_hp_volume_offset_info, + .get = st_hp_volume_offset_get, + .put = st_hp_volume_offset_put, + }, }; static void xonar_line_mic_ac97_switch(struct oxygen *chip, @@ -671,7 +750,16 @@ static int xonar_d2_mixer_init(struct oxygen *chip) static int xonar_st_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip)); + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(st_controls); ++i) { + err = snd_ctl_add(chip->card, + snd_ctl_new1(&st_controls[i], chip)); + if (err < 0) + return err; + } + return 0; } static const struct oxygen_model model_xonar_d2 = { -- cgit v1.2.2 From 76ffe1e3fb2f65e98d7ed001c5a2b6f334655364 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:20:11 +0200 Subject: sound: oxygen: allow custom MCLK rates Add a callback that allows model drivers to modify the default I2S MCLK rate. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/hifier.c | 1 + sound/pci/oxygen/oxygen.c | 1 + sound/pci/oxygen/oxygen.h | 4 ++++ sound/pci/oxygen/oxygen_pcm.c | 13 +++++++++---- sound/pci/oxygen/xonar_cs43xx.c | 1 + sound/pci/oxygen/xonar_pcm179x.c | 3 +++ 6 files changed, 19 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index 2079c100aabc..e3c229b63311 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -170,6 +170,7 @@ static const struct oxygen_model model_hifier = { .init = hifier_init, .cleanup = hifier_cleanup, .resume = hifier_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_ak4396_params, .set_adc_params = set_cs5340_params, .update_dac_volume = update_ak4396_volume, diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index c986c5ebf65b..d12fd9efe94e 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -361,6 +361,7 @@ static const struct oxygen_model model_generic = { .init = generic_init, .cleanup = generic_cleanup, .resume = generic_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_ak4396_params, .set_adc_params = set_wm8785_params, .update_dac_volume = update_ak4396_volume, diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 2ac3b3c8253f..6147216af744 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -78,6 +78,8 @@ struct oxygen_model { void (*resume)(struct oxygen *chip); void (*pcm_hardware_filter)(unsigned int channel, struct snd_pcm_hardware *hardware); + unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel, + struct snd_pcm_hw_params *hw_params); void (*set_dac_params)(struct oxygen *chip, struct snd_pcm_hw_params *params); void (*set_adc_params)(struct oxygen *chip, @@ -163,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip); /* oxygen_pcm.c */ int oxygen_pcm_init(struct oxygen *chip); +unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel, + struct snd_pcm_hw_params *hw_params); /* oxygen_io.c */ diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index 1e98333366df..9dff6954c397 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -271,13 +271,16 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params) } } -static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params) +unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, + unsigned int channel, + struct snd_pcm_hw_params *hw_params) { if (params_rate(hw_params) <= 96000) return OXYGEN_I2S_MCLK_256; else return OXYGEN_I2S_MCLK_128; } +EXPORT_SYMBOL(oxygen_default_i2s_mclk); static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) { @@ -354,7 +357,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream, OXYGEN_REC_FORMAT_A_MASK); oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, oxygen_rate(hw_params) | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_A, hw_params) | chip->model.adc_i2s_format | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | @@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, if (!is_ac97) oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, oxygen_rate(hw_params) | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_B, + hw_params) | chip->model.adc_i2s_format | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | @@ -472,7 +476,8 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, oxygen_rate(hw_params) | chip->model.dac_i2s_format | - oxygen_i2s_mclk(hw_params) | + chip->model.get_i2s_mclk(chip, PCM_MULTICH, + hw_params) | oxygen_i2s_bits(hw_params), OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_FORMAT_MASK | diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 330c5e755917..a83f827feb34 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -321,6 +321,7 @@ static const struct oxygen_model model_xonar_d1 = { .cleanup = xonar_d1_cleanup, .suspend = xonar_d1_suspend, .resume = xonar_d1_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_cs43xx_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_cs43xx_volume, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index cf94e4432a3f..35b3fb4071fb 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -771,6 +771,7 @@ static const struct oxygen_model model_xonar_d2 = { .cleanup = xonar_d2_cleanup, .suspend = xonar_d2_suspend, .resume = xonar_d2_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_pcm1796_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -801,6 +802,7 @@ static const struct oxygen_model model_xonar_hdav = { .suspend = xonar_hdav_suspend, .resume = xonar_hdav_resume, .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_hdav_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -831,6 +833,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, + .get_i2s_mclk = oxygen_default_i2s_mclk, .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, -- cgit v1.2.2 From 973dca93a3d46cca7e4743300f8a510b779906af Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:20:47 +0200 Subject: sound: virtuoso: add PCM1796 oversampling control Add a control to increase the oversampling factor to 128x on cards with PCM1796 or PCM1792A DACs. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/xonar_pcm179x.c | 182 +++++++++++++++++++++++++++++++++------ 1 file changed, 157 insertions(+), 25 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 35b3fb4071fb..7f153fb1848d 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -173,8 +173,11 @@ struct xonar_pcm179x { struct xonar_generic generic; unsigned int dacs; u8 pcm1796_regs[4][5]; + unsigned int current_rate; + bool os_128; bool hp_active; s8 hp_gain_offset; + bool has_cs2000; u8 cs2000_fun_cfg_1; }; @@ -277,6 +280,7 @@ static void pcm1796_init(struct oxygen *chip) PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; pcm1796_registers_init(chip); + data->current_rate = 48000; } static void xonar_d2_init(struct oxygen *chip) @@ -401,6 +405,7 @@ static void xonar_st_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; + data->has_cs2000 = 1; data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1; oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, @@ -486,18 +491,57 @@ static void xonar_st_resume(struct oxygen *chip) xonar_stx_resume(chip); } -static void set_pcm1796_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) +static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate) +{ + struct xonar_pcm179x *data = chip->model_data; + + if (rate <= 32000) + return OXYGEN_I2S_MCLK_512; + else if (rate <= 48000 && data->os_128) + return OXYGEN_I2S_MCLK_512; + else if (rate <= 96000) + return OXYGEN_I2S_MCLK_256; + else + return OXYGEN_I2S_MCLK_128; +} + +static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip, + unsigned int channel, + struct snd_pcm_hw_params *params) +{ + if (channel == PCM_MULTICH) + return mclk_from_rate(chip, params_rate(params)); + else + return oxygen_default_i2s_mclk(chip, channel, params); +} + +static void update_pcm1796_oversampling(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; unsigned int i; u8 reg; - reg = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + if (data->current_rate <= 32000) + reg = PCM1796_OS_128; + else if (data->current_rate <= 48000 && data->os_128) + reg = PCM1796_OS_128; + else if (data->current_rate <= 96000 || data->os_128) + reg = PCM1796_OS_64; + else + reg = PCM1796_OS_32; for (i = 0; i < data->dacs; ++i) pcm1796_write_cached(chip, i, 20, reg); } +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct xonar_pcm179x *data = chip->model_data; + + data->current_rate = params_rate(params); + update_pcm1796_oversampling(chip); +} + static void update_pcm1796_volume(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; @@ -526,26 +570,44 @@ static void update_pcm1796_mute(struct oxygen *chip) pcm1796_write_cached(chip, i, 18, value); } -static void set_cs2000_params(struct oxygen *chip, - struct snd_pcm_hw_params *params) +static void update_cs2000_rate(struct oxygen *chip, unsigned int rate) { - /* XXX Why is the I2S A MCLK half the actual I2S multich MCLK? */ - static const u8 rate_mclks[] = { - [OXYGEN_RATE_32000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_44100] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_48000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128, - [OXYGEN_RATE_64000] = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_88200] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_96000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_176400] = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256, - [OXYGEN_RATE_192000] = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256, - }; - unsigned int rate_index; + struct xonar_pcm179x *data = chip->model_data; u8 rate_mclk, reg; - rate_index = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) - & OXYGEN_I2S_RATE_MASK; - rate_mclk = rate_mclks[rate_index]; + switch (rate) { + /* XXX Why is the I2S A MCLK half the actual I2S MCLK? */ + case 32000: + rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; + break; + case 44100: + if (data->os_128) + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + else + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128; + break; + default: /* 48000 */ + if (data->os_128) + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + else + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128; + break; + case 64000: + rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256; + break; + case 88200: + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + break; + case 96000: + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + break; + case 176400: + rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256; + break; + case 192000: + rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256; + break; + } oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk, OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK); if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128) @@ -558,7 +620,7 @@ static void set_cs2000_params(struct oxygen *chip, static void set_st_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { - set_cs2000_params(chip, params); + update_cs2000_rate(chip, params_rate(params)); set_pcm1796_params(chip, params); } @@ -580,6 +642,59 @@ static const struct snd_kcontrol_new alt_switch = { .private_value = GPIO_D2_ALT, }; +static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { "64x", "128x" }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int os_128_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + value->value.enumerated.item[0] = data->os_128; + return 0; +} + +static int os_128_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != data->os_128; + if (changed) { + data->os_128 = value->value.enumerated.item[0]; + if (data->has_cs2000) + update_cs2000_rate(chip, data->current_rate); + oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, + mclk_from_rate(chip, data->current_rate), + OXYGEN_I2S_MCLK_MASK); + update_pcm1796_oversampling(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new os_128_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Oversampling Playback Enum", + .info = os_128_info, + .get = os_128_get, + .put = os_128_put, +}; + static int st_output_switch_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { @@ -745,7 +860,20 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) static int xonar_d2_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; + return 0; +} + +static int xonar_hdav_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); } static int xonar_st_mixer_init(struct oxygen *chip) @@ -759,6 +887,9 @@ static int xonar_st_mixer_init(struct oxygen *chip) if (err < 0) return err; } + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; return 0; } @@ -771,7 +902,7 @@ static const struct oxygen_model model_xonar_d2 = { .cleanup = xonar_d2_cleanup, .suspend = xonar_d2_suspend, .resume = xonar_d2_resume, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_pcm1796_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -798,11 +929,12 @@ static const struct oxygen_model model_xonar_hdav = { .longname = "Asus Virtuoso 200", .chip = "AV200", .init = xonar_hdav_init, + .mixer_init = xonar_hdav_mixer_init, .cleanup = xonar_hdav_cleanup, .suspend = xonar_hdav_suspend, .resume = xonar_hdav_resume, .pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_hdav_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, @@ -833,7 +965,7 @@ static const struct oxygen_model model_xonar_st = { .cleanup = xonar_st_cleanup, .suspend = xonar_st_suspend, .resume = xonar_st_resume, - .get_i2s_mclk = oxygen_default_i2s_mclk, + .get_i2s_mclk = get_pcm1796_i2s_mclk, .set_dac_params = set_st_params, .set_adc_params = xonar_set_cs53x1_params, .update_dac_volume = update_pcm1796_volume, -- cgit v1.2.2 From 4852ad02476ab2bbc874f6f8fda9e677e0f09c87 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:21:21 +0200 Subject: sound: oxygen: add digital filter control Add a control to select between sharp and slow roll-of filter responses of the DACs. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen.c | 65 ++++++++++++++++++++++++++++++ sound/pci/oxygen/xonar_cs43xx.c | 82 +++++++++++++++++++++++++++++++++++--- sound/pci/oxygen/xonar_pcm179x.c | 85 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 223 insertions(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index d12fd9efe94e..3ad9eb00aebd 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -352,6 +352,70 @@ static void set_ak5385_params(struct oxygen *chip, value, GPIO_AK5385_DFS_MASK); } +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Sharp Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + unsigned int i; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->ak4396_regs[0][AK4396_CONTROL_2]; + if (value->value.enumerated.item[0]) + reg |= AK4396_SLOW; + else + reg &= ~AK4396_SLOW; + changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2]; + if (changed) { + for (i = 0; i < 4; ++i) + ak4396_write(chip, i, AK4396_CONTROL_2, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + +static int generic_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); +} + static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static const struct oxygen_model model_generic = { @@ -359,6 +423,7 @@ static const struct oxygen_model model_generic = { .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = generic_init, + .mixer_init = generic_mixer_init, .cleanup = generic_cleanup, .resume = generic_resume, .get_i2s_mclk = oxygen_default_i2s_mclk, diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index a83f827feb34..16c226bfcd2b 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -69,7 +69,7 @@ struct xonar_cs43xx { struct xonar_generic generic; - u8 cs4398_regs[7]; + u8 cs4398_regs[8]; u8 cs4362a_regs[15]; }; @@ -121,12 +121,11 @@ static void cs43xx_registers_init(struct oxygen *chip) cs4398_write(chip, 4, data->cs4398_regs[4]); cs4398_write(chip, 5, data->cs4398_regs[5]); cs4398_write(chip, 6, data->cs4398_regs[6]); - cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP | - CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4398_write(chip, 7, data->cs4398_regs[7]); 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, 0x04, data->cs4362a_regs[0x04]); cs4362a_write(chip, 0x05, 0); for (i = 6; i <= 14; ++i) cs4362a_write(chip, i, data->cs4362a_regs[i]); @@ -147,6 +146,9 @@ static void xonar_d1_init(struct oxygen *chip) CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE; data->cs4398_regs[5] = 60 * 2; data->cs4398_regs[6] = 60 * 2; + data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP | + CS4398_ZERO_CROSS | CS4398_SOFT_RAMP; + data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE; data->cs4362a_regs[6] = CS4362A_FM_SINGLE | CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; data->cs4362a_regs[7] = 60 | CS4362A_MUTE; @@ -286,6 +288,68 @@ static const struct snd_kcontrol_new front_panel_switch = { .private_value = GPIO_D1_FRONT_PANEL, }; +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Fast Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_cs43xx *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->cs4398_regs[7] & CS4398_FILT_SEL) != 0; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_cs43xx *data = chip->model_data; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->cs4398_regs[7]; + if (value->value.enumerated.item[0]) + reg |= CS4398_FILT_SEL; + else + reg &= ~CS4398_FILT_SEL; + changed = reg != data->cs4398_regs[7]; + if (changed) { + cs4398_write(chip, 7, reg); + if (reg & CS4398_FILT_SEL) + reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL; + else + reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL; + cs4362a_write(chip, 0x04, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip, unsigned int reg, unsigned int mute) { @@ -309,7 +373,15 @@ static int xonar_d1_control_filter(struct snd_kcontrol_new *template) static int xonar_d1_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); + if (err < 0) + return err; + return 0; } static const struct oxygen_model model_xonar_d1 = { diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 7f153fb1848d..ba18fb546b4f 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -265,7 +265,8 @@ static void pcm1796_registers_init(struct oxygen *chip) + gain_offset); pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1] + gain_offset); - pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 19, + data->pcm1796_regs[0][19 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 20, data->pcm1796_regs[0][20 - PCM1796_REG_BASE]); pcm1796_write(chip, i, 21, 0); @@ -278,6 +279,8 @@ static void pcm1796_init(struct oxygen *chip) data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD; + data->pcm1796_regs[0][19 - PCM1796_REG_BASE] = + PCM1796_FLT_SHARP | PCM1796_ATS_1; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64; pcm1796_registers_init(chip); data->current_rate = 48000; @@ -642,6 +645,67 @@ static const struct snd_kcontrol_new alt_switch = { .private_value = GPIO_D2_ALT, }; +static int rolloff_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "Sharp Roll-off", "Slow Roll-off" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int rolloff_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->pcm1796_regs[0][19 - PCM1796_REG_BASE] & + PCM1796_FLT_MASK) != PCM1796_FLT_SHARP; + return 0; +} + +static int rolloff_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->pcm1796_regs[0][19 - PCM1796_REG_BASE]; + reg &= ~PCM1796_FLT_MASK; + if (!value->value.enumerated.item[0]) + reg |= PCM1796_FLT_SHARP; + else + reg |= PCM1796_FLT_SLOW; + changed = reg != data->pcm1796_regs[0][19 - PCM1796_REG_BASE]; + if (changed) { + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 19, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new rolloff_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DAC Filter Playback Enum", + .info = rolloff_info, + .get = rolloff_get, + .put = rolloff_put, +}; + static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) { static const char *const names[2] = { "64x", "128x" }; @@ -858,6 +922,19 @@ static int xonar_st_control_filter(struct snd_kcontrol_new *template) return 0; } +static int add_pcm1796_controls(struct oxygen *chip) +{ + int err; + + err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + if (err < 0) + return err; + return 0; +} + static int xonar_d2_mixer_init(struct oxygen *chip) { int err; @@ -865,7 +942,7 @@ static int xonar_d2_mixer_init(struct oxygen *chip) err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); if (err < 0) return err; - err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + err = add_pcm1796_controls(chip); if (err < 0) return err; return 0; @@ -873,7 +950,7 @@ static int xonar_d2_mixer_init(struct oxygen *chip) static int xonar_hdav_mixer_init(struct oxygen *chip) { - return snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + return add_pcm1796_controls(chip); } static int xonar_st_mixer_init(struct oxygen *chip) @@ -887,7 +964,7 @@ static int xonar_st_mixer_init(struct oxygen *chip) if (err < 0) return err; } - err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip)); + err = add_pcm1796_controls(chip); if (err < 0) return err; return 0; -- cgit v1.2.2 From 1ff048869eb8e8408856e23b3dc6af094491f837 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:21:51 +0200 Subject: sound: oxygen: add high-pass filter control Add a control that allows disabling the high-pass filter of the WM8785 ADC. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index 3ad9eb00aebd..acbedebcffd9 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids); struct generic_data { u8 ak4396_regs[4][5]; - u16 wm8785_regs[1]; + u16 wm8785_regs[3]; }; static void ak4396_write(struct oxygen *chip, unsigned int codec, @@ -184,6 +184,7 @@ static void wm8785_registers_init(struct oxygen *chip) wm8785_write(chip, WM8785_R7, 0); wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]); + wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); } static void wm8785_init(struct oxygen *chip) @@ -192,6 +193,7 @@ static void wm8785_init(struct oxygen *chip) data->wm8785_regs[0] = WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST; + data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL; wm8785_registers_init(chip); snd_component_add(chip->card, "WM8785"); } @@ -334,6 +336,7 @@ static void set_wm8785_params(struct oxygen *chip, if (value != data->wm8785_regs[0]) { wm8785_write(chip, WM8785_R7, 0); wm8785_write(chip, WM8785_R0, value); + wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]); } } @@ -411,11 +414,75 @@ static const struct snd_kcontrol_new rolloff_control = { .put = rolloff_put, }; +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "None", "High-pass Filter" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + + value->value.enumerated.item[0] = + (data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0; + return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct generic_data *data = chip->model_data; + unsigned int reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL); + if (value->value.enumerated.item[0]) + reg |= WM8785_HPFR | WM8785_HPFL; + changed = reg != data->wm8785_regs[WM8785_R2]; + if (changed) + wm8785_write(chip, WM8785_R2, reg); + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new hpf_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Filter Capture Enum", + .info = hpf_info, + .get = hpf_get, + .put = hpf_put, +}; + static int generic_mixer_init(struct oxygen *chip) { return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip)); } +static int generic_wm8785_mixer_init(struct oxygen *chip) +{ + int err; + + err = generic_mixer_init(chip); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip)); + if (err < 0) + return err; + return 0; +} + static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); static const struct oxygen_model model_generic = { @@ -423,7 +490,7 @@ static const struct oxygen_model model_generic = { .longname = "C-Media Oxygen HD Audio", .chip = "CMI8788", .init = generic_init, - .mixer_init = generic_mixer_init, + .mixer_init = generic_wm8785_mixer_init, .cleanup = generic_cleanup, .resume = generic_resume, .get_i2s_mclk = oxygen_default_i2s_mclk, @@ -455,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip, switch (id->driver_data) { case MODEL_MERIDIAN: chip->model.init = meridian_init; + chip->model.mixer_init = generic_mixer_init; chip->model.resume = meridian_resume; chip->model.set_adc_params = set_ak5385_params; chip->model.device_config = PLAYBACK_0_TO_I2S | @@ -470,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip, break; case MODEL_CLARO_HALO: chip->model.init = claro_halo_init; + chip->model.mixer_init = generic_mixer_init; chip->model.cleanup = claro_cleanup; chip->model.suspend = claro_suspend; chip->model.resume = claro_resume; -- cgit v1.2.2 From 62428f7b8c873d43be8201e66392c3aad82fec93 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 28 Sep 2009 11:22:18 +0200 Subject: sound: oxygen: fix input monitor control names Insert "Playback" into the input monitor control names to prevent alsa-lib from treating these controls as global controls. Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai --- sound/pci/oxygen/oxygen_mixer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 5dfb5fb73381..f375b8a27862 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -805,7 +805,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -813,7 +813,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, @@ -830,7 +830,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -838,7 +838,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, @@ -855,7 +855,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Switch", + .name = "Analog Input Monitor Playback Switch", .index = 1, .info = snd_ctl_boolean_mono_info, .get = monitor_get, @@ -864,7 +864,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Input Monitor Volume", + .name = "Analog Input Monitor Playback Volume", .index = 1, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, @@ -882,7 +882,7 @@ static const struct { .controls = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Monitor Switch", + .name = "Digital Input Monitor Playback Switch", .info = snd_ctl_boolean_mono_info, .get = monitor_get, .put = monitor_put, @@ -890,7 +890,7 @@ static const struct { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Monitor Volume", + .name = "Digital Input Monitor Playback Volume", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, .info = monitor_volume_info, -- cgit v1.2.2 From 71623855e20c3febebb5fa60528cde2592678bd5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Sep 2009 13:14:04 +0200 Subject: ALSA: hda - Enable MSI as default Since the recent kernel can handle MSI properly on non-Intel platforms, let's enable MSI as default. If any borken device is found, we can add the quirk entry to the list, which is currently empty. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c9ad182e1b4b..d0effa3563e2 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -60,7 +60,7 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int single_cmd; -static int enable_msi; +static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; #endif @@ -2300,11 +2300,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) } /* - * white-list for enable_msi + * white/black-list for enable_msi */ -static struct snd_pci_quirk msi_white_list[] __devinitdata = { - SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1), - SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1), +static struct snd_pci_quirk msi_black_list[] __devinitdata = { {} }; @@ -2312,10 +2310,12 @@ static void __devinit check_msi(struct azx *chip) { const struct snd_pci_quirk *q; - chip->msi = enable_msi; - if (chip->msi) + if (enable_msi >= 0) { + chip->msi = !!enable_msi; return; - q = snd_pci_quirk_lookup(chip->pci, msi_white_list); + } + chip->msi = 1; /* enable MSI as default */ + q = snd_pci_quirk_lookup(chip->pci, msi_black_list); if (q) { printk(KERN_INFO "hda_intel: msi for device %04x:%04x set to %d\n", -- cgit v1.2.2 From 0afe5f891501609f31146798fb41784f4adad27c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 2 Oct 2009 09:20:00 +0200 Subject: ALSA: hda - Clean up name string creation in patch_realtek.c Use a common helper to create playback controls. This gives less chance of typos. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 137 ++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 80 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7810d3dcad83..a751858811e1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4309,6 +4309,20 @@ static int add_control(struct alc_spec *spec, int type, const char *name, return 0; } +static int add_control_with_pfx(struct alc_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, unsigned long val) +{ + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, val); +} + +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val) + #define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17) #define alc880_fixed_pin_idx(nid) ((nid) - 0x14) #define alc880_is_multi_pin(nid) ((nid) >= 0x18) @@ -4362,7 +4376,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - char name[32]; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; @@ -4375,26 +4388,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i])); if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); if (err < 0) @@ -4406,14 +4419,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "Speaker"; else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) @@ -4429,7 +4440,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, { hda_nid_t nid; int err; - char name[32]; if (!pin) return 0; @@ -4443,21 +4453,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, spec->multiout.extra_out_nid[0] = nid; /* control HP volume/switch on the output mixer amp */ nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) return err; } else if (alc880_is_multi_pin(pin)) { /* set manual connection */ /* we have only a switch on HP-out PIN */ - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -4470,16 +4477,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname, int idx, hda_nid_t mix_nid) { - char name[32]; int err; - sprintf(name, "%s Playback Volume", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", ctlname); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -5972,7 +5976,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, { hda_nid_t nid_vol; unsigned long vol_val, sw_val; - char name[32]; int err; if (nid >= 0x0f && nid < 0x11) { @@ -5992,14 +5995,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, if (!(*vol_bits & (1 << nid_vol))) { /* first control for the volume widget */ - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val); + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val); if (err < 0) return err; *vol_bits |= (1 << nid_vol); } - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val); + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val); if (err < 0) return err; return 1; @@ -10936,7 +10937,6 @@ static int alc262_check_volbit(hda_nid_t nid) static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, const char *pfx, int *vbits) { - char name[32]; unsigned long val; int vbit; @@ -10946,28 +10946,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid, if (*vbits & vbit) /* a volume control for this mixer already there */ return 0; *vbits |= vbit; - snprintf(name, sizeof(name), "%s Playback Volume", pfx); if (vbit == 2) val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT); - return add_control(spec, ALC_CTL_WIDGET_VOL, name, val); + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val); } static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid, const char *pfx) { - char name[32]; unsigned long val; if (!nid) return 0; - snprintf(name, sizeof(name), "%s Playback Switch", pfx); if (nid == 0x16) val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val); + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val); } /* add playback controls from the parsed DAC table */ @@ -12305,11 +12302,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = { static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) { - char name[32]; hda_nid_t dac; int err; - sprintf(name, "%s Playback Volume", ctlname); switch (nid) { case 0x14: case 0x16: @@ -12323,7 +12318,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, } if (spec->multiout.dac_nids[0] != dac && spec->multiout.dac_nids[1] != dac) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, HDA_COMPOSE_AMP_VAL(dac, 3, idx, HDA_OUTPUT)); if (err < 0) @@ -12331,12 +12326,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac; } - sprintf(name, "%s Playback Switch", ctlname); if (nid != 0x16) - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); else /* mono */ - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT)); if (err < 0) return err; @@ -12366,8 +12360,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->speaker_pins[0]; if (nid == 0x1d) { - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Speaker Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); if (err < 0) return err; @@ -12385,8 +12378,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; if (nid == 0x16) { - err = add_control(spec, ALC_CTL_WIDGET_MUTE, - "Mono Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono", HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -14235,9 +14227,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec, static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } @@ -15360,7 +15350,6 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { - char name[32]; static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"}; hda_nid_t nid_v, nid_s; int i, err; @@ -15377,26 +15366,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (i == 2) { /* Center/LFE */ - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "Center Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "Center", HDA_COMPOSE_AMP_VAL(nid_v, 1, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_WIDGET_VOL, - "LFE Playback Volume", + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, + "LFE", HDA_COMPOSE_AMP_VAL(nid_v, 2, 0, HDA_OUTPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "Center Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "Center", HDA_COMPOSE_AMP_VAL(nid_s, 1, 2, HDA_INPUT)); if (err < 0) return err; - err = add_control(spec, ALC_CTL_BIND_MUTE, - "LFE Playback Switch", + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, + "LFE", HDA_COMPOSE_AMP_VAL(nid_s, 2, 2, HDA_INPUT)); if (err < 0) @@ -15411,8 +15400,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, pfx = "PCM"; } else pfx = chname[i]; - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) @@ -15420,8 +15408,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) pfx = "Speaker"; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) @@ -15439,7 +15426,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, { hda_nid_t nid_v, nid_s; int err; - char name[32]; if (!pin) return 0; @@ -15457,21 +15443,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec, nid_s = alc861vd_idx_to_mixer_switch( alc880_fixed_pin_idx(pin)); - sprintf(name, "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) return err; } else if (alc880_is_multi_pin(pin)) { /* set manual connection */ /* we have only a switch on HP-out PIN */ - sprintf(name, "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -17213,21 +17196,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec, return 0; } -static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, +static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - sprintf(name, "%s Playback Volume", pfx); - return add_control(spec, ALC_CTL_WIDGET_VOL, name, + return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); } -static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, +static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx, hda_nid_t nid, unsigned int chs) { - char name[32]; - sprintf(name, "%s Playback Switch", pfx); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT)); } @@ -17305,13 +17284,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, return 0; nid = alc662_look_for_dac(codec, pin); if (!nid) { - char name[32]; /* the corresponding DAC is already occupied */ if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) return 0; /* no way */ /* create a switch only */ - sprintf(name, "%s Playback Switch", pfx); - return add_control(spec, ALC_CTL_WIDGET_MUTE, name, + return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); } -- cgit v1.2.2 From b6153e1175a46db9dde17d12609adba7d72330b9 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:23 +0800 Subject: ALSA: HDA VIA: Remove unused IS_VT17xx_VENDORID macro IS_VT17*_VENDORID macros are used nowhere, so clean them up. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index ee89db90c9b6..9dfe1b55970c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -76,14 +76,6 @@ #define VT1702_HP_NID 0x17 #define VT1702_DIGOUT_NID 0x11 -#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) -#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) -#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) -#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) -#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) -#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397) -#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398) - enum VIA_HDA_CODEC { UNKNOWN = -1, VT1708, -- cgit v1.2.2 From 744ff5f487925223beb6e21460c8cec468b54ab4 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:26 +0800 Subject: ALSA: HDA VIA: Change get_codec_type argument to hda_codec type Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9dfe1b55970c..e7d739f12247 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -88,8 +88,9 @@ enum VIA_HDA_CODEC { CODEC_TYPES, }; -static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) +static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { + u32 vendor_id = codec->vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -141,7 +142,7 @@ static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); - if (get_codec_type(codec->vendor_id) == VT1708S + if (get_codec_type(codec) == VT1708S && (nid == 0x1a || nid == 0x1e)) { if (size < 4 * sizeof(unsigned int)) return -ENOMEM; @@ -163,7 +164,7 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol); - if (get_codec_type(codec->vendor_id) == VT1708S + if (get_codec_type(codec) == VT1708S && (nid == 0x1a || nid == 0x1e)) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; -- cgit v1.2.2 From 518bf3ba753ad93644e7c6cf95c043c918d9429b Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:29 +0800 Subject: ALSA: HDA VIA: Add VT1708B-CE codec support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e7d739f12247..4d9ffd6f190b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -84,6 +84,7 @@ enum VIA_HDA_CODEC { VT1708B_8CH, VT1708B_4CH, VT1708S, + VT1708BCE, VT1702, CODEC_TYPES, }; @@ -104,9 +105,11 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1709_10CH; else if (dev_id >= 0xe714 && dev_id <= 0xe717) codec_type = VT1709_6CH; - else if (dev_id >= 0xe720 && dev_id <= 0xe723) + else if (dev_id >= 0xe720 && dev_id <= 0xe723) { codec_type = VT1708B_8CH; - else if (dev_id >= 0xe724 && dev_id <= 0xe727) + if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) + codec_type = VT1708BCE; + } else if (dev_id >= 0xe724 && dev_id <= 0xe727) codec_type = VT1708B_4CH; else if ((dev_id & 0xfff) == 0x397 && (dev_id >> 12) < 8) @@ -224,6 +227,8 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; + enum VIA_HDA_CODEC codec_type; + #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -979,6 +984,10 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + spec->codec_type = get_codec_type(codec); + if (spec->codec_type == VT1708BCE) + spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost + same */ /* Lydia Add for EAPD enable */ if (!spec->dig_in_nid) { /* No Digital In connection */ if (spec->dig_in_pin) { @@ -2369,12 +2378,14 @@ static struct hda_amp_list vt1708B_loopbacks[] = { { } /* end */ }; #endif - +static int patch_vt1708S(struct hda_codec *codec); static int patch_vt1708B_8ch(struct hda_codec *codec) { struct via_spec *spec; int err; + if (get_codec_type(codec) == VT1708BCE) + return patch_vt1708S(codec); /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -2906,6 +2917,16 @@ static int patch_vt1708S(struct hda_codec *codec) spec->loopback.amplist = vt1708S_loopbacks; #endif + /* correct names for VT1708BCE */ + if (get_codec_type(codec) == VT1708BCE) { + kfree(codec->chip_name); + codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); + spec->stream_name_analog = "VT1708BCE Analog"; + spec->stream_name_digital = "VT1708BCE Digital"; + } return 0; } -- cgit v1.2.2 From c2c02ea326d3683f551120e74a297b354a223357 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:32 +0800 Subject: ALSA: HDA VIA: Limit VT1702 AA-Path max volume according to customer request, VT1702 AA-Path max volume (12 dB) is too high, so limit to 0 dB. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4d9ffd6f190b..e62698984287 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -3166,6 +3166,12 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); if (err < 0) return err; + /* limit AA path volume to 0 dB */ + snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) return err; -- cgit v1.2.2 From f5271101faf1655d862849f42518c2a88ef394fb Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:35 +0800 Subject: ALSA HDA VIA: Add VIA_CTL_WIDGET_ANALOG_MUTE control type Enter low power state if AA-Path volume is muted. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 240 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e62698984287..d6bee620ced6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -128,6 +128,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, + VIA_CTL_WIDGET_ANALOG_MUTE, }; enum { @@ -177,9 +178,34 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, return 0; } +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); +static void set_jack_power_state(struct hda_codec *codec); + +static int analog_input_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + set_jack_power_state(codec); + analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + return change; +} + +/* modify .put = snd_hda_mixer_amp_switch_put */ +#define ANALOG_INPUT_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = analog_input_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + static struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + ANALOG_INPUT_MUTE, }; @@ -303,7 +329,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, if (err < 0) return err; sprintf(name, "%s Playback Switch", ctlname); - err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; @@ -362,6 +388,131 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } + +static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, + unsigned int *affected_parm) +{ + unsigned parm; + unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); + unsigned no_presence = (def_conf & AC_DEFCFG_MISC) + >> AC_DEFCFG_MISC_SHIFT + & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ + unsigned present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31; + + if ((no_presence || present) && get_defcfg_connect(def_conf) + != AC_JACK_PORT_NONE) { + *affected_parm = AC_PWRST_D0; /* if it's connected */ + parm = AC_PWRST_D0; + } else + parm = AC_PWRST_D3; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); +} + +static void set_jack_power_state(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int imux_is_smixer; + unsigned int parm; + + if (spec->codec_type == VT1702) { + imux_is_smixer = snd_hda_codec_read( + codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 1/2/5 (14h/15h/18h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x14, &parm); + set_pin_power_state(codec, 0x15, &parm); + set_pin_power_state(codec, 0x18, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ + /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW 3/4 (16h/17h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x16, &parm); + set_pin_power_state(codec, 0x17, &parm); + /* MW0 (1ah), AOW 0/1 (10h/1dh) */ + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, + parm); + } else if (spec->codec_type == VT1708B_8CH + || spec->codec_type == VT1708B_4CH + || spec->codec_type == VT1708S) { + /* SW0 (17h) = stereo mixer */ + int is_8ch = spec->codec_type != VT1708B_4CH; + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) + == ((spec->codec_type == VT1708S) ? 5 : 0); + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW 0/1 (13h/14h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW6 (22h), SW2 (26h), AOW2 (24h) */ + if (is_8ch) { + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x22, &parm); + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x24, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* PW 3/4/7 (1ch/1dh/23h) */ + parm = AC_PWRST_D3; + /* force to D0 for internal Speaker */ + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + if (is_8ch) + set_pin_power_state(codec, 0x23, &parm); + /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + if (is_8ch) { + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x27, 0, + AC_VERB_SET_POWER_STATE, parm); + } + } +} + /* * input MUX handling */ @@ -504,6 +655,93 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { }, { } /* end */ }; + +/* check AA path's mute statue */ +static int is_aa_path_mute(struct hda_codec *codec) +{ + int mute = 1; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + struct via_spec *spec = codec->spec; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + case VT1702: + nid_mixer = 0x1a; + start_idx = 1; + end_idx = 3; + break; + default: + return 0; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + unsigned int con_list = snd_hda_codec_read( + codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); + int shift = 8 * (i % 4); + hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; + unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); + if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { + /* check mute status while the pin is connected */ + int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, + HDA_INPUT, i) >> 7; + int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, + HDA_INPUT, i) >> 7; + if (!mute_l || !mute_r) { + mute = 0; + break; + } + } + } + return mute; +} + +/* enter/exit analog low-current mode */ +static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) +{ + struct via_spec *spec = codec->spec; + static int saved_stream_idle = 1; /* saved stream idle status */ + int enable = is_aa_path_mute(codec); + unsigned int verb = 0; + unsigned int parm = 0; + + if (stream_idle == -1) /* stream status did not change */ + enable = enable && saved_stream_idle; + else { + enable = enable && stream_idle; + saved_stream_idle = stream_idle; + } + + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + default: + return; /* other codecs are not supported */ + } + /* send verb */ + snd_hda_codec_write(codec, codec->afg, 0, verb, parm); +} + /* * generic initialization of ADC, input mixers and output mixers */ -- cgit v1.2.2 From 173143791068ac9f155c378a591d0b3d6c4a45ca Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:37 +0800 Subject: ALSA: HDA VIA: Add low current mode for power saving. For VT1708B, VT1708S and VT1702, enter low current mode if no analog stream is opened and all aa path mute. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index d6bee620ced6..7ace0fca933d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -783,6 +783,10 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); } @@ -1089,6 +1093,11 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* init power states */ + set_jack_power_state(codec); + analog_low_current_mode(codec, 1); + via_free_kctls(codec); /* no longer needed */ return 0; } @@ -2312,6 +2321,17 @@ static struct hda_verb vt1708B_uniwill_init_verbs[] = { { } }; +static int via_pcm_open_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + int idle = substream->pstr->substream_opened == 1 + && substream->ref_count == 0; + + analog_low_current_mode(codec, idle); + return 0; +} + static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .substreams = 2, .channels_min = 2, @@ -2320,7 +2340,8 @@ static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2342,8 +2363,10 @@ static struct hda_pcm_stream vt1708B_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2800,7 +2823,8 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .cleanup = via_playback_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -2810,8 +2834,10 @@ static struct hda_pcm_stream vt1708S_pcm_analog_capture = { .channels_max = 2, .nid = 0x13, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -3236,7 +3262,8 @@ static struct hda_pcm_stream vt1702_pcm_analog_playback = { .ops = { .open = via_playback_pcm_open, .prepare = via_playback_multi_pcm_prepare, - .cleanup = via_playback_multi_pcm_cleanup + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close }, }; @@ -3246,8 +3273,10 @@ static struct hda_pcm_stream vt1702_pcm_analog_capture = { .channels_max = 2, .nid = 0x12, /* NID to query formats and rates */ .ops = { + .open = via_pcm_open_close, .prepare = via_capture_pcm_prepare, - .cleanup = via_capture_pcm_cleanup + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close }, }; -- cgit v1.2.2 From 9510e8dd9cb4469d146953270364af6dd86a39be Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:39 +0800 Subject: ALSA: HDA VIA: Remove unused argument of via_new_analog_input Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 7ace0fca933d..0da57db3a691 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -317,8 +317,8 @@ static void via_free_kctls(struct hda_codec *codec) } /* create input playback/capture controls for the given pin */ -static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin, - const char *ctlname, int idx, int mix_nid) +static int via_new_analog_input(struct via_spec *spec, const char *ctlname, + int idx, int mix_nid) { char name[32]; int err; @@ -1480,8 +1480,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x17); + err = via_new_analog_input(spec, labels[i], idx, 0x17); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2014,8 +2013,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x18); + err = via_new_analog_input(spec, labels[i], idx, 0x18); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -2576,8 +2574,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -3048,8 +3045,7 @@ static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, idx = 1; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], - idx, 0x16); + err = via_new_analog_input(spec, labels[i], idx, 0x16); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; @@ -3402,8 +3398,7 @@ static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, idx = 3; break; } - err = via_new_analog_input(spec, cfg->input_pins[i], - labels[i], idx, 0x1A); + err = via_new_analog_input(spec, labels[i], idx, 0x1A); if (err < 0) return err; imux->items[imux->num_items].label = labels[i]; -- cgit v1.2.2 From 0713efebfa1a1878feeeb17cbadc3d2d2c9e9ed2 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:43 +0800 Subject: ALSA: HDA VIA: Change VT1708S & VT1702 hp mode controls For VT1708S and VT1702, deactivate "Headphone Playback Volume" and "Headphone Playback Mute" control if "Independent HP" mode is OFF. and rename VT1702 "Independent HP" text. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0da57db3a691..9e8dd57e8d5c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -572,6 +572,18 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, return 0; } +static void activate_ctl(struct hda_codec *codec, const char *name, int active) +{ + struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); + if (ctl) { + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access |= active + ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + } +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -620,6 +632,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, spec->multiout.hp_nid, 0, 0, 0); + /* update HP volume/swtich active state */ + if (spec->codec_type == VT1708S + || spec->codec_type == VT1702) { + activate_ctl(codec, "Headphone Playback Volume", + spec->hp_independent_mode); + activate_ctl(codec, "Headphone Playback Switch", + spec->hp_independent_mode); + } return 0; } @@ -3342,11 +3362,11 @@ static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) { - int err; - + int err, i; + struct hda_input_mux *imux; + static const char *texts[] = { "ON", "OFF", NULL}; if (!pin) return 0; - spec->multiout.hp_nid = 0x1D; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, @@ -3361,8 +3381,18 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) if (err < 0) return err; - create_hp_imux(spec); + imux = &spec->private_imux[1]; + /* for hp mode select */ + i = 0; + while (texts[i] != NULL) { + imux->items[imux->num_items].label = texts[i]; + imux->items[imux->num_items].index = i; + imux->num_items++; + i++; + } + + spec->hp_mux = &spec->private_imux[1]; return 0; } -- cgit v1.2.2 From cdc1784d49258198df600fbc1d37c07d7eee5ed6 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:47 +0800 Subject: ALSA: HDA VIA: Rewrite via_independent_hp_put Use hp_independent_mode_index to store hp index, and simplify function via_independent_hp_put with it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 85 +++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 39 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9e8dd57e8d5c..e3bd5261986e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -252,6 +252,7 @@ struct via_spec { /* HP mode source */ const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; + unsigned int hp_independent_mode_index; enum VIA_HDA_CODEC codec_type; @@ -584,6 +585,36 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active) } } +static int update_side_mute_status(struct hda_codec *codec) +{ + /* mute side channel */ + struct via_spec *spec = codec->spec; + unsigned int parm = spec->hp_independent_mode + ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; + hda_nid_t sw3; + + switch (spec->codec_type) { + case VT1708: + sw3 = 0x1b; + break; + case VT1709_10CH: + sw3 = 0x29; + break; + case VT1708B_8CH: + case VT1708S: + sw3 = 0x27; + break; + default: + sw3 = 0; + break; + } + + if (sw3) + snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm); + return 0; +} + static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -591,47 +622,18 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, struct via_spec *spec = codec->spec; hda_nid_t nid = spec->autocfg.hp_pins[0]; unsigned int pinsel = ucontrol->value.enumerated.item[0]; - unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - - if (con_nid == spec->multiout.hp_nid) { - if (pinsel == 0) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } else if (pinsel == 1) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } - } else { - if (pinsel == 0) { - if (spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs += 1; - spec->hp_independent_mode = 0; - } - } else if (pinsel == 1) { - if (!spec->hp_independent_mode) { - if (spec->multiout.num_dacs > 1) - spec->multiout.num_dacs -= 1; - spec->hp_independent_mode = 1; - } - } - } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - pinsel); + /* Get Independent Mode index of headphone pin widget */ + spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel + ? 1 : 0; - if (spec->multiout.hp_nid && - spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) - snd_hda_codec_setup_stream(codec, - spec->multiout.hp_nid, - 0, 0, 0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + + if (spec->multiout.hp_nid && spec->multiout.hp_nid + != spec->multiout.dac_nids[HDA_FRONT]) + snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, + 0, 0, 0); + update_side_mute_status(codec); /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S || spec->codec_type == VT1702) { @@ -1447,6 +1449,7 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -1982,6 +1985,7 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) spec->multiout.hp_nid = VT1709_HP_DAC_NID; else if (spec->multiout.num_dacs == 3) /* 6 channels */ spec->multiout.hp_nid = 0; + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -2541,6 +2545,7 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -3011,6 +3016,7 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) return 0; spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ + spec->hp_independent_mode_index = 1; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", @@ -3368,6 +3374,7 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) if (!pin) return 0; spec->multiout.hp_nid = 0x1D; + spec->hp_independent_mode_index = 0; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Headphone Playback Volume", -- cgit v1.2.2 From 1564b2878f5cf160f60af99d4dbca1dd7809ee8a Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:52 +0800 Subject: ALSA: HDA VIA: Add smart5.1 function. Smart 5.1 is for 3-jacks model, to reuse input pins as outputs. While off, they act as "line out" / "line in" / "mic in". While on, they acts as "line out" / "back left/right" / "center/lfe". Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 177 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e3bd5261986e..26ee1c3a4d16 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -211,7 +211,7 @@ static struct snd_kcontrol_new vt1708_control_templates[] = { struct via_spec { /* codec parameterization */ - struct snd_kcontrol_new *mixers[3]; + struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; struct hda_verb *init_verbs[5]; @@ -253,6 +253,7 @@ struct via_spec { const struct hda_input_mux *hp_mux; unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; + unsigned int smart51_enabled; enum VIA_HDA_CODEC codec_type; @@ -390,6 +391,8 @@ static void via_auto_init_analog_input(struct hda_codec *codec) } } +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); + static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -400,9 +403,10 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ unsigned present = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) >> 31; - - if ((no_presence || present) && get_defcfg_connect(def_conf) - != AC_JACK_PORT_NONE) { + struct via_spec *spec = codec->spec; + if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) + || ((no_presence || present) + && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ parm = AC_PWRST_D0; } else @@ -657,6 +661,167 @@ static struct snd_kcontrol_new via_hp_mixer[] = { { } /* end */ }; +static void notify_aa_path_ctls(struct hda_codec *codec) +{ + int i; + struct snd_ctl_elem_id id; + const char *labels[] = {"Mic", "Front Mic", "Line"}; + + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + for (i = 0; i < ARRAY_SIZE(labels); i++) { + sprintf(id.name, "%s Playback Volume", labels[i]); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +static void mute_aa_path(struct hda_codec *codec, int mute) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid_mixer; + int start_idx; + int end_idx; + int i; + /* get nid of MW0 and start & end index */ + switch (spec->codec_type) { + case VT1708: + nid_mixer = 0x17; + start_idx = 2; + end_idx = 4; + break; + case VT1709_10CH: + case VT1709_6CH: + nid_mixer = 0x18; + start_idx = 2; + end_idx = 4; + break; + case VT1708B_8CH: + case VT1708B_4CH: + case VT1708S: + nid_mixer = 0x16; + start_idx = 2; + end_idx = 4; + break; + default: + return; + } + /* check AA path's mute status */ + for (i = start_idx; i <= end_idx; i++) { + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; + snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, + HDA_AMP_MUTE, val); + } +} +static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) +{ + int res = 0; + int index; + for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { + if (pin == spec->autocfg.input_pins[index]) { + res = 1; + break; + } + } + return res; +} + +static int via_smart51_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 via_smart51_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int on = 1; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (nid) { + int ctl = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0); + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode) + continue; /* ignore FMic for independent HP */ + if (ctl & AC_PINCTL_IN_EN + && !(ctl & AC_PINCTL_OUT_EN)) + on = 0; + } + } + *ucontrol->value.integer.value = on; + return 0; +} + +static int via_smart51_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int out_in = *ucontrol->value.integer.value + ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + int i; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + hda_nid_t nid = spec->autocfg.input_pins[index[i]]; + if (i == AUTO_PIN_FRONT_MIC + && spec->hp_independent_mode) + continue; /* don't retask FMic for independent HP */ + if (nid) { + unsigned int parm = snd_hda_codec_read( + codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + } + if (i == AUTO_PIN_FRONT_MIC) { + if (spec->codec_type == VT1708S) { + /* input = index 1 (AOW3) */ + snd_hda_codec_write( + codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, 1); + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, + 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); + } + } + } + spec->smart51_enabled = *ucontrol->value.integer.value; + set_jack_power_state(codec); + return 1; +} + +static struct snd_kcontrol_new via_smart51_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = via_smart51_info, + .get = via_smart51_get, + .put = via_smart51_put, + }, + {} /* end */ +}; + /* capture mixer elements */ static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -1587,6 +1752,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2087,6 +2253,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -2649,6 +2816,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } @@ -3142,6 +3310,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) if (spec->hp_mux) spec->mixers[spec->num_mixers++] = via_hp_mixer; + spec->mixers[spec->num_mixers++] = via_smart51_mixer; return 1; } -- cgit v1.2.2 From a80e6e3c8c21ca50837e2e42fa438a4ff4a9788e Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:07:55 +0800 Subject: ALSA: HDA VIA: When changing input source, update power state. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 26ee1c3a4d16..c5e99944990a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -549,6 +549,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, if (!spec->mux_nids[adc_idx]) return -EINVAL; + /* switch to D0 beofre change index */ + if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + /* update jack power state */ + set_jack_power_state(codec); + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); -- cgit v1.2.2 From a34df19a658170fb7125e8017ee46ba54b1ad495 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:01 +0800 Subject: ALSA: HDA VIA: Add VIA_JACK_EVENT process in via_unsol_event. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c5e99944990a..cd62c88b5246 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -124,6 +124,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 +#define VIA_JACK_EVENT 0x04 enum { VIA_CTL_WIDGET_VOL, @@ -1413,10 +1414,12 @@ static void via_unsol_event(struct hda_codec *codec, unsigned int res) { res >>= 26; - if (res == VIA_HP_EVENT) + if (res & VIA_HP_EVENT) via_hp_automute(codec); - else if (res == VIA_GPIO_EVENT) + if (res & VIA_GPIO_EVENT) via_gpio_control(codec); + if (res & VIA_JACK_EVENT) + set_jack_power_state(codec); } static int via_init(struct hda_codec *codec) @@ -1878,7 +1881,8 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = { }; static struct hda_verb vt1709_uniwill_init_verbs[] = { - {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, { } }; @@ -2514,7 +2518,15 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { }; static struct hda_verb vt1708B_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -3009,7 +3021,15 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { }; static struct hda_verb vt1708S_uniwill_init_verbs[] = { - {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; @@ -3448,8 +3468,12 @@ static struct hda_verb vt1702_volume_init_verbs[] = { }; static struct hda_verb vt1702_uniwill_init_verbs[] = { - {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, { } }; -- cgit v1.2.2 From dcf34c8cc685781cebbe1f4c75272a3269eba3a1 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:15 +0800 Subject: ALSA: HDA VIA: Refresh front playback mute in via_hp_automute. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index cd62c88b5246..c1f4307feaae 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1351,14 +1351,25 @@ static void via_free(struct hda_codec *codec) /* mute internal speaker if HP is plugged */ static void via_hp_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = 0; struct via_spec *spec = codec->spec; present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], - HDA_OUTPUT, 0, HDA_AMP_MUTE, - present ? HDA_AMP_MUTE : 0); + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + /* auto mute */ + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Front Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } } static void via_gpio_control(struct hda_codec *codec) -- cgit v1.2.2 From 1f2e99febd5dd0c91f0d0752674029a4376649e5 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:17 +0800 Subject: ALSA: HDA VIA: Add Jack detect feature for VT1708. VT1708 does not support unsolicited response, but we need hp detect to automute speaker. Implemented in workqueue. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 230 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 173 insertions(+), 57 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c1f4307feaae..38418a53acd7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -89,6 +89,64 @@ enum VIA_HDA_CODEC { CODEC_TYPES, }; +struct via_spec { + /* codec parameterization */ + struct snd_kcontrol_new *mixers[4]; + unsigned int num_mixers; + + struct hda_verb *init_verbs[5]; + unsigned int num_iverbs; + + char *stream_name_analog; + struct hda_pcm_stream *stream_analog_playback; + struct hda_pcm_stream *stream_analog_capture; + + char *stream_name_digital; + struct hda_pcm_stream *stream_digital_playback; + struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + hda_nid_t slave_dig_outs[2]; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t *adc_nids; + hda_nid_t mux_nids[3]; + hda_nid_t dig_in_nid; + hda_nid_t dig_in_pin; + + /* capture source */ + const struct hda_input_mux *input_mux; + unsigned int cur_mux[3]; + + /* PCM information */ + struct hda_pcm pcm_rec[3]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_imux[2]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + + /* HP mode source */ + const struct hda_input_mux *hp_mux; + unsigned int hp_independent_mode; + unsigned int hp_independent_mode_index; + unsigned int smart51_enabled; + + enum VIA_HDA_CODEC codec_type; + + /* work to check hp jack state */ + struct hda_codec *codec; + struct delayed_work vt1708_hp_work; + int vt1708_jack_detectect; + int vt1708_hp_present; +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif +}; + static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { u32 vendor_id = codec->vendor_id; @@ -181,6 +239,31 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); static void set_jack_power_state(struct hda_codec *codec); +static int is_aa_path_mute(struct hda_codec *codec); + +static void vt1708_start_hp_work(struct via_spec *spec) +{ + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + if (!delayed_work_pending(&spec->vt1708_hp_work)) + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); +} + +static void vt1708_stop_hp_work(struct via_spec *spec) +{ + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) + return; + if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 + && !is_aa_path_mute(spec->codec)) + return; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, + !spec->vt1708_jack_detectect); + cancel_delayed_work(&spec->vt1708_hp_work); + flush_scheduled_work(); +} static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -190,6 +273,12 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, set_jack_power_state(codec); analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); + if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { + if (is_aa_path_mute(codec)) + vt1708_start_hp_work(codec->spec); + else + vt1708_stop_hp_work(codec->spec); + } return change; } @@ -210,59 +299,6 @@ static struct snd_kcontrol_new vt1708_control_templates[] = { }; -struct via_spec { - /* codec parameterization */ - struct snd_kcontrol_new *mixers[4]; - unsigned int num_mixers; - - struct hda_verb *init_verbs[5]; - unsigned int num_iverbs; - - char *stream_name_analog; - struct hda_pcm_stream *stream_analog_playback; - struct hda_pcm_stream *stream_analog_capture; - - char *stream_name_digital; - struct hda_pcm_stream *stream_digital_playback; - struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; - hda_nid_t slave_dig_outs[2]; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t *adc_nids; - hda_nid_t mux_nids[3]; - hda_nid_t dig_in_nid; - hda_nid_t dig_in_pin; - - /* capture source */ - const struct hda_input_mux *input_mux; - unsigned int cur_mux[3]; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; - struct snd_array kctls; - struct hda_input_mux private_imux[2]; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - - /* HP mode source */ - const struct hda_input_mux *hp_mux; - unsigned int hp_independent_mode; - unsigned int hp_independent_mode_index; - unsigned int smart51_enabled; - - enum VIA_HDA_CODEC codec_type; - -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct hda_loopback_check loopback; -#endif -}; - static hda_nid_t vt1708_adc_nids[2] = { /* ADC1-2 */ 0x15, 0x27 @@ -981,7 +1017,6 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, struct via_spec *spec = codec->spec; int idle = substream->pstr->substream_opened == 1 && substream->ref_count == 0; - analog_low_current_mode(codec, idle); return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); @@ -994,6 +1029,7 @@ static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + vt1708_start_hp_work(spec); return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); } @@ -1003,6 +1039,7 @@ static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } @@ -1094,7 +1131,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); } - + vt1708_start_hp_work(spec); return 0; } @@ -1134,7 +1171,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); } - + vt1708_stop_hp_work(spec); return 0; } @@ -1345,6 +1382,7 @@ static void via_free(struct hda_codec *codec) return; via_free_kctls(codec); + vt1708_stop_hp_work(spec); kfree(codec->spec); } @@ -1464,6 +1502,15 @@ static int via_init(struct hda_codec *codec) return 0; } +#ifdef SND_HDA_NEEDS_RESUME +static int via_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); + return 0; +} +#endif + #ifdef CONFIG_SND_HDA_POWER_SAVE static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -1479,6 +1526,9 @@ static struct hda_codec_ops via_patch_ops = { .build_pcms = via_build_pcms, .init = via_init, .free = via_free, +#ifdef SND_HDA_NEEDS_RESUME + .suspend = via_suspend, +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = via_check_power_status, #endif @@ -1728,6 +1778,51 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) return; } +static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = + !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); + ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; + return 0; +} + +static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int change; + + if (spec->codec_type != VT1708) + return 0; + spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; + change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) + == !spec->vt1708_jack_detectect; + if (spec->vt1708_jack_detectect) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + return change; +} + +static struct snd_kcontrol_new vt1708_jack_detectect[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Jack Detect", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = vt1708_jack_detectect_get, + .put = vt1708_jack_detectect_put, + }, + {} /* end */ +}; + static int vt1708_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1753,6 +1848,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + /* add jack detect on/off control */ + err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); if (err < 0) return err; @@ -1788,6 +1887,22 @@ static int via_auto_init(struct hda_codec *codec) return 0; } +static void vt1708_update_hp_jack_state(struct work_struct *work) +{ + struct via_spec *spec = container_of(work, struct via_spec, + vt1708_hp_work.work); + if (spec->codec_type != VT1708) + return; + /* if jack state toggled */ + if (spec->vt1708_hp_present + != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) >> 31)) { + spec->vt1708_hp_present ^= 1; + via_hp_automute(spec->codec); + } + vt1708_start_hp_work(spec); +} + static int get_mux_nids(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1864,7 +1979,8 @@ static int patch_vt1708(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif - + spec->codec = codec; + INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } -- cgit v1.2.2 From 82ef9e45c48634af5e3f6ab9ac75b6642c538020 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:19 +0800 Subject: ALSA: HDA VIA: Modify vt1708_set_pinconfig_connect function. like seqassoc 0xff, seqassoc 0xf0 of vt1708 should override Port Connectivity field into 'AC_JACK_PORT_COMPLEX' Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 38418a53acd7..dc416ec0c6d4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1768,11 +1768,10 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) def_conf = snd_hda_codec_get_pincfg(codec, nid); seqassoc = (unsigned char) get_defcfg_association(def_conf); seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { - if (seqassoc == 0xff) { - def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); - snd_hda_codec_set_pincfg(codec, nid, def_conf); - } + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE + && (seqassoc == 0xf0 || seqassoc == 0xff)) { + def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); + snd_hda_codec_set_pincfg(codec, nid, def_conf); } return; -- cgit v1.2.2 From c873cc25280113d71463ad5075413d283be6b766 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:21 +0800 Subject: ALSA: HDA VIA: Replace via_playback_pcm_prepare/cleanup Replaced with via_playback_multi_pcm_prepare/cleanup to support multi-stream operations Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index dc416ec0c6d4..4d3c447342b0 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1022,28 +1022,6 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, hinfo); } -static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - vt1708_start_hp_work(spec); - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(spec); - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - - static void playback_multi_pcm_prep_0(struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -1252,7 +1230,7 @@ static struct hda_pcm_stream vt1708_pcm_analog_playback = { }; static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { - .substreams = 1, + .substreams = 2, .channels_min = 2, .channels_max = 8, .nid = 0x10, /* NID to query formats and rates */ @@ -1263,8 +1241,8 @@ static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { .formats = SNDRV_PCM_FMTBIT_S16_LE, .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup }, }; @@ -2062,8 +2040,8 @@ static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -2074,8 +2052,8 @@ static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, }, }; @@ -3166,8 +3144,8 @@ static struct hda_pcm_stream vt1708S_pcm_analog_playback = { .nid = 0x10, /* NID to query formats and rates */ .ops = { .open = via_playback_pcm_open, - .prepare = via_playback_pcm_prepare, - .cleanup = via_playback_pcm_cleanup, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, .close = via_pcm_open_close }, }; -- cgit v1.2.2 From 9645c2039d5cfdbdcebe297420e180b6cd262836 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:27 +0800 Subject: ALSA: HDA VIA: Modify vt1708_auto_create_multi_out_ctls. Rewrite nid_vol/mute assignment for clearity, and check line connection before adding control for it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4d3c447342b0..efadacd60835 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1553,7 +1553,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid, nid_vol = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { @@ -1562,8 +1562,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; - if (i != AUTO_SEQ_FRONT) - nid_vol = 0x18 + i; + nid_vol = nid_vols[i]; if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ @@ -1595,13 +1594,13 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; -- cgit v1.2.2 From 4483a2f5907fa824bd6384c36fdcee9777cab1b9 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:29 +0800 Subject: ALSA: HDA VIA: Modify vt1709_auto_create_multi_out_ctls. Rewrite nid_vol/mute assignment for clearity, and check line connection before adding control for it. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index efadacd60835..f9702a17fc16 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2160,7 +2160,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, { char name[32]; static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; - hda_nid_t nid = 0; + hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; int i, err; for (i = 0; i <= AUTO_SEQ_SIDE; i++) { @@ -2169,43 +2169,45 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; + nid_vol = nid_vols[i]; + if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ - /* add control to mixer index 0 */ + /* ADD control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_INPUT)); if (err < 0) return err; @@ -2226,26 +2228,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, } else if (i == AUTO_SEQ_SURROUND) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SIDE) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); if (err < 0) return err; -- cgit v1.2.2 From 6369bcfccb57da28ad3e09b25fecd841a415ae95 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:31 +0800 Subject: ALSA: HDA VIA: Replace MIC_BOOST_VOLUME. With snd_hda_override_amp_caps. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 72 ++++++++++------------------------------------- 1 file changed, 15 insertions(+), 57 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f9702a17fc16..4b7cd5971701 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -197,46 +197,6 @@ enum { AUTO_SEQ_SIDE }; -/* Some VT1708S based boards gets the micboost setting wrong, so we have - * to apply some brute-force and re-write the TLV's by software. */ -static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *_tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - - if (get_codec_type(codec) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - if (size < 4 * sizeof(unsigned int)) - return -ENOMEM; - if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */ - return -EFAULT; - if (put_user(2 * sizeof(unsigned int), _tlv + 1)) - return -EFAULT; - if (put_user(0, _tlv + 2)) /* offset = 0 */ - return -EFAULT; - if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ - return -EFAULT; - } - return 0; -} - -static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - hda_nid_t nid = get_amp_nid(kcontrol); - - if (get_codec_type(codec) == VT1708S - && (nid == 0x1a || nid == 0x1e)) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 3; - } - return 0; -} - static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); static void set_jack_power_state(struct hda_codec *codec); static int is_aa_path_mute(struct hda_codec *codec); @@ -3063,29 +3023,15 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) /* Patch for VT1708S */ -/* VT1708S software backdoor based override for buggy hardware micboost - * setting */ -#define MIC_BOOST_VOLUME(xname, nid) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = 0, \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ - .info = mic_boost_volume_info, \ - .get = snd_hda_mixer_amp_volume_get, \ - .put = snd_hda_mixer_amp_volume_put, \ - .tlv = { .c = mic_boost_tlv }, \ - .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } - /* capture mixer elements */ static struct snd_kcontrol_new vt1708S_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), - MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), - MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer @@ -3457,6 +3403,16 @@ static struct hda_amp_list vt1708S_loopbacks[] = { }; #endif +static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, + int offset, int num_steps, int step_size) +{ + snd_hda_override_amp_caps(codec, pin, HDA_INPUT, + (offset << AC_AMPCAP_OFFSET_SHIFT) | + (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | + (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); +} + static int patch_vt1708S(struct hda_codec *codec) { struct via_spec *spec; @@ -3493,6 +3449,8 @@ static int patch_vt1708S(struct hda_codec *codec) spec->adc_nids = vt1708S_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; spec->num_mixers++; } -- cgit v1.2.2 From bc7e7e5ce05047e16633a94d36fa144af1d2b4c7 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:32 +0800 Subject: ALSA: HDA VIA: Move backdoor verbs to vt17xx_volume_init_verb As init verbs, vt17xx_volume_init_verb is a better place to hold them. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4b7cd5971701..1c87231fa7e5 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -3068,6 +3068,8 @@ static struct hda_verb vt1708S_volume_init_verbs[] = { {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* Enable Mic Boost Volume backdoor */ {0x1, 0xf98, 0x1}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, { } }; @@ -3527,6 +3529,10 @@ static struct hda_verb vt1702_volume_init_verbs[] = { /* PW6 PW7 Output enable */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* mixer enable */ + {0x1, 0xF88, 0x3}, + /* GPIO 0~2 */ + {0x1, 0xF82, 0x3F}, { } }; @@ -3768,8 +3774,6 @@ static int patch_vt1702(struct hda_codec *codec) { struct via_spec *spec; int err; - unsigned int response; - unsigned char control; /* create a codec specific record */ spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -3814,18 +3818,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->loopback.amplist = vt1702_loopbacks; #endif - /* Open backdoor */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); - control = (unsigned char)(response & 0xff); - control |= 0x3; - snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); - - /* Enable GPIO 0&1 for volume&mute control */ - /* Enable GPIO 2 for DMIC-DATA */ - response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); - control = (unsigned char)((response >> 16) & 0x3f); - snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); - return 0; } -- cgit v1.2.2 From eb7188cafcb7aa1419b8889494cdbd4e6a01da1c Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:34 +0800 Subject: ALSA: HDA VIA: Add VT1718S support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 554 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 545 insertions(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 1c87231fa7e5..c78385340694 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -86,6 +86,7 @@ enum VIA_HDA_CODEC { VT1708S, VT1708BCE, VT1702, + VT1718S, CODEC_TYPES, }; @@ -175,6 +176,9 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x398 && (dev_id >> 12) < 8) codec_type = VT1702; + else if ((dev_id & 0xfff) == 0x428 + && (dev_id >> 12) < 8) + codec_type = VT1718S; else codec_type = UNKNOWN; return codec_type; @@ -284,6 +288,11 @@ static hda_nid_t vt1702_adc_nids[3] = { 0x12, 0x20, 0x1F }; +static hda_nid_t vt1718S_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -512,6 +521,67 @@ static void set_jack_power_state(struct hda_codec *codec) snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); } + } else if (spec->codec_type == VT1718S) { + /* MUX6 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x27, &parm); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW2 (26h), AOW2 (ah) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW0/1 (24h/25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + set_pin_power_state(codec, 0x25, &parm); + if (!spec->hp_independent_mode) /* check for redirected HP */ + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, + parm); + /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ + snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + if (spec->hp_independent_mode) { + /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x1b, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0xc, 0, + AC_VERB_SET_POWER_STATE, parm); + } } } @@ -572,11 +642,21 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.hp_pins[0]; - unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, - 0x00); + hda_nid_t nid; + unsigned int pinsel; + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + /* use !! to translate conn sel 2 for VT1718S */ + pinsel = !!snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, + 0x00); ucontrol->value.enumerated.item[0] = pinsel; return 0; @@ -635,6 +715,16 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ + spec->multiout.num_dacs = 4; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid @@ -645,7 +735,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, update_side_mute_status(codec); /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S - || spec->codec_type == VT1702) { + || spec->codec_type == VT1702 + || spec->codec_type == VT1718S) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -758,7 +849,8 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode) + && spec->hp_independent_mode + && spec->codec_type != VT1718S) continue; /* ignore FMic for independent HP */ if (ctl & AC_PINCTL_IN_EN && !(ctl & AC_PINCTL_OUT_EN)) @@ -782,7 +874,8 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, for (i = 0; i < ARRAY_SIZE(index); i++) { hda_nid_t nid = spec->autocfg.input_pins[index[i]]; if (i == AUTO_PIN_FRONT_MIC - && spec->hp_independent_mode) + && spec->hp_independent_mode + && spec->codec_type != VT1718S) continue; /* don't retask FMic for independent HP */ if (nid) { unsigned int parm = snd_hda_codec_read( @@ -797,6 +890,10 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, mute_aa_path(codec, 1); notify_aa_path_ctls(codec); } + if (spec->codec_type == VT1718S) + snd_hda_codec_amp_stereo( + codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, + HDA_AMP_UNMUTE); } if (i == AUTO_PIN_FRONT_MIC) { if (spec->codec_type == VT1708S) { @@ -871,6 +968,11 @@ static int is_aa_path_mute(struct hda_codec *codec) start_idx = 1; end_idx = 3; break; + case VT1718S: + nid_mixer = 0x21; + start_idx = 1; + end_idx = 3; + break; default: return 0; } @@ -920,6 +1022,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ break; case VT1708S: + case VT1718S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; @@ -1026,8 +1129,8 @@ static void playback_multi_pcm_prep_0(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); - if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && - !spec->hp_independent_mode) + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] + && !spec->hp_independent_mode) /* headphone out will just decode front left/right (stereo) */ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); @@ -3821,6 +3924,435 @@ static int patch_vt1702(struct hda_codec *codec) return 0; } +/* Patch for VT1718S */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1718S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1718S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + + /* Setup default input of Front HP to MW9 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* PW9 PW10 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + /* PW11 Input enable */ + {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf88, 0x8}, + /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Unmute MW4's index 0 */ + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { } +}; + + +static struct hda_verb vt1718S_uniwill_init_verbs[] = { + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 10, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1718S_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 4; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x8; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0xa; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x9; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0xb; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; + hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; + hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; + hda_nid_t nid, nid_vol, nid_mute = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + /* Center/LFE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + /* Front */ + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0xc; /* AOW4 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 1; + break; + + case 0x2a: /* Line In */ + idx = 2; + break; + + case 0x29: /* Front Mic */ + idx = 3; + break; + + case 0x2c: /* CD */ + idx = 0; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + return 0; +} + +static int vt1718S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + + if (err < 0) + return err; + err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) + spec->dig_in_nid = 0x13; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1718S_loopbacks[] = { + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { 0x21, HDA_INPUT, 3 }, + { 0x21, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1718S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1718S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; + + spec->stream_name_analog = "VT1718S Analog"; + spec->stream_analog_playback = &vt1718S_pcm_analog_playback; + spec->stream_analog_capture = &vt1718S_pcm_analog_capture; + + spec->stream_name_digital = "VT1718S Digital"; + spec->stream_digital_playback = &vt1718S_pcm_digital_playback; + if (codec->vendor_id == 0x11060428) + spec->stream_digital_capture = &vt1718S_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1718S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1718S_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -3893,6 +4425,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1702}, { .id = 0x11067398, .name = "VT1702", .patch = patch_vt1702}, + { .id = 0x11060428, .name = "VT1718S", + .patch = patch_vt1718S}, + { .id = 0x11064428, .name = "VT1718S", + .patch = patch_vt1718S}, {} /* terminator */ }; -- cgit v1.2.2 From bb3c6bfc3f7a5416d85c5dbc312e2d47fc672eef Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:39 +0800 Subject: ALSA: HDA VIA: Add VT1828S and VT2020 support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c78385340694..2e7e72c83a52 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -179,6 +179,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x428 && (dev_id >> 12) < 8) codec_type = VT1718S; + else if (dev_id == 0x0441 || dev_id == 0x4441) + codec_type = VT1718S; else codec_type = UNKNOWN; return codec_type; @@ -4323,21 +4325,31 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; - spec->stream_name_analog = "VT1718S Analog"; + if (codec->vendor_id == 0x11060441) + spec->stream_name_analog = "VT2020 Analog"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_analog = "VT1828S Analog"; + else + spec->stream_name_analog = "VT1718S Analog"; spec->stream_analog_playback = &vt1718S_pcm_analog_playback; spec->stream_analog_capture = &vt1718S_pcm_analog_capture; - spec->stream_name_digital = "VT1718S Digital"; + if (codec->vendor_id == 0x11060441) + spec->stream_name_digital = "VT2020 Digital"; + else if (codec->vendor_id == 0x11064441) + spec->stream_name_digital = "VT1828S Digital"; + else + spec->stream_name_digital = "VT1718S Digital"; spec->stream_digital_playback = &vt1718S_pcm_digital_playback; - if (codec->vendor_id == 0x11060428) + if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) spec->stream_digital_capture = &vt1718S_pcm_digital_capture; if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1718S_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); get_mux_nids(codec); - override_mic_boost(codec, 0x1a, 0, 3, 40); - override_mic_boost(codec, 0x1e, 0, 3, 40); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; spec->num_mixers++; } @@ -4429,6 +4441,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1718S}, { .id = 0x11064428, .name = "VT1718S", .patch = patch_vt1718S}, + { .id = 0x11060441, .name = "VT2020", + .patch = patch_vt1718S}, + { .id = 0x11064441, .name = "VT1828S", + .patch = patch_vt1718S}, {} /* terminator */ }; -- cgit v1.2.2 From f3db423df84570c9950754a5771ad26f0111235f Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:41 +0800 Subject: ALSA: HDA VIA: Add VT1716S support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 648 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 644 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2e7e72c83a52..2977004677ec 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -87,12 +87,13 @@ enum VIA_HDA_CODEC { VT1708BCE, VT1702, VT1718S, + VT1716S, CODEC_TYPES, }; struct via_spec { /* codec parameterization */ - struct snd_kcontrol_new *mixers[4]; + struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; struct hda_verb *init_verbs[5]; @@ -135,7 +136,7 @@ struct via_spec { unsigned int hp_independent_mode; unsigned int hp_independent_mode_index; unsigned int smart51_enabled; - + unsigned int dmic_enabled; enum VIA_HDA_CODEC codec_type; /* work to check hp jack state */ @@ -179,6 +180,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) else if ((dev_id & 0xfff) == 0x428 && (dev_id >> 12) < 8) codec_type = VT1718S; + else if (dev_id == 0x0433 || dev_id == 0xa721) + codec_type = VT1716S; else if (dev_id == 0x0441 || dev_id == 0x4441) codec_type = VT1718S; else @@ -189,6 +192,7 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_HP_EVENT 0x01 #define VIA_GPIO_EVENT 0x02 #define VIA_JACK_EVENT 0x04 +#define VIA_MONO_EVENT 0x08 enum { VIA_CTL_WIDGET_VOL, @@ -295,6 +299,11 @@ static hda_nid_t vt1718S_adc_nids[2] = { 0x10, 0x11 }; +static hda_nid_t vt1716S_adc_nids[2] = { + /* ADC1-2 */ + 0x13, 0x14 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -584,6 +593,106 @@ static void set_jack_power_state(struct hda_codec *codec) snd_hda_codec_write(codec, 0xc, 0, AC_VERB_SET_POWER_STATE, parm); } + } else if (spec->codec_type == VT1716S) { + unsigned int mono_out, present; + /* SW0 (17h) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 1/2/5 (1ah/1bh/1eh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1a, &parm); + set_pin_power_state(codec, 0x1b, &parm); + set_pin_power_state(codec, 0x1e, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* SW0 (17h), AIW0(13h) */ + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, + parm); + + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1e, &parm); + /* PW11 (22h) */ + if (spec->dmic_enabled) + set_pin_power_state(codec, 0x22, &parm); + else + snd_hda_codec_write( + codec, 0x22, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + /* SW2(26h), AIW1(14h) */ + snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* outputs */ + /* PW0 (19h), SW1 (18h), AOW1 (11h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x19, &parm); + /* Smart 5.1 PW2(1bh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1b, &parm); + snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW7 (23h), SW3 (27h), AOW3 (25h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x23, &parm); + /* Smart 5.1 PW1(1ah) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1a, &parm); + snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Smart 5.1 PW5(1eh) */ + if (spec->smart51_enabled) + set_pin_power_state(codec, 0x1e, &parm); + snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* Mono out */ + /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ + present = snd_hda_codec_read( + codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) + mono_out = 0; + else { + present = snd_hda_codec_read( + codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) + & 0x80000000; + if (!spec->hp_independent_mode && present) + mono_out = 0; + else + mono_out = 1; + } + parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; + snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, + parm); + snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, + parm); + + /* PW 3/4 (1ch/1dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x1c, &parm); + set_pin_power_state(codec, 0x1d, &parm); + /* HP Independent Mode, power on AOW3 */ + if (spec->hp_independent_mode) + snd_hda_codec_write(codec, 0x25, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* force to D0 for internal Speaker */ + /* MW0 (16h), AOW0 (10h) */ + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, + imux_is_smixer ? AC_PWRST_D0 : parm); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, + mono_out ? AC_PWRST_D0 : parm); } } @@ -738,7 +847,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* update HP volume/swtich active state */ if (spec->codec_type == VT1708S || spec->codec_type == VT1702 - || spec->codec_type == VT1718S) { + || spec->codec_type == VT1718S + || spec->codec_type == VT1716S) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -797,6 +907,7 @@ static void mute_aa_path(struct hda_codec *codec, int mute) case VT1708B_8CH: case VT1708B_4CH: case VT1708S: + case VT1716S: nid_mixer = 0x16; start_idx = 2; end_idx = 4; @@ -898,7 +1009,8 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, HDA_AMP_UNMUTE); } if (i == AUTO_PIN_FRONT_MIC) { - if (spec->codec_type == VT1708S) { + if (spec->codec_type == VT1708S + || spec->codec_type == VT1716S) { /* input = index 1 (AOW3) */ snd_hda_codec_write( codec, nid, 0, @@ -961,6 +1073,7 @@ static int is_aa_path_mute(struct hda_codec *codec) case VT1708B_8CH: case VT1708B_4CH: case VT1708S: + case VT1716S: nid_mixer = 0x16; start_idx = 2; end_idx = 4; @@ -1025,6 +1138,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) break; case VT1708S: case VT1718S: + case VT1716S: verb = 0xf73; parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ break; @@ -1453,6 +1567,36 @@ static void via_hp_automute(struct hda_codec *codec) } } +/* mute mono out if HP or Line out is plugged */ +static void via_mono_automute(struct hda_codec *codec) +{ + unsigned int hp_present, lineout_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT1716S) + return; + + lineout_present = snd_hda_codec_read( + codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + /* Mute Mono Out if Line Out is plugged */ + if (lineout_present) { + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); + return; + } + + hp_present = snd_hda_codec_read( + codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) + snd_hda_codec_amp_stereo( + codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, + hp_present ? HDA_AMP_MUTE : 0); +} + static void via_gpio_control(struct hda_codec *codec) { unsigned int gpio_data; @@ -1512,6 +1656,8 @@ static void via_unsol_event(struct hda_codec *codec, via_gpio_control(codec); if (res & VIA_JACK_EVENT) set_jack_power_state(codec); + if (res & VIA_MONO_EVENT) + via_mono_automute(codec); } static int via_init(struct hda_codec *codec) @@ -4365,6 +4511,496 @@ static int patch_vt1718S(struct hda_codec *codec) return 0; } + +/* Patch for VT1716S */ + +static int vt1716s_dmic_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 vt1716s_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int index = 0; + + index = snd_hda_codec_read(codec, 0x26, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (index != -1) + *ucontrol->value.integer.value = index; + + return 0; +} + +static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int index = *ucontrol->value.integer.value; + + snd_hda_codec_write(codec, 0x26, 0, + AC_VERB_SET_CONNECT_SEL, index); + spec->dmic_enabled = index; + set_jack_power_state(codec); + + return 1; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1716S_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Mic Capture Switch", + .count = 1, + .info = vt1716s_dmic_info, + .get = vt1716s_dmic_get, + .put = vt1716s_dmic_put, + }, + {} /* end */ +}; + + +/* mono-out mixer elements */ +static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { + HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static struct hda_verb vt1716S_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Stereo Mixer = 5 */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, + + /* Setup default input of PW4 to MW0 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, + + /* Setup default input of SW1 as MW0 */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* Setup default input of SW4 as AOW0 */ + {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, + + /* PW9 PW10 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + + /* Unmute SW1, PW12 */ + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* PW12 Output enable */ + {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Enable Boost Volume backdoor */ + {0x1, 0xf8a, 0x80}, + /* don't bybass mixer */ + {0x1, 0xf88, 0xc0}, + /* Enable mono output */ + {0x1, 0xf90, 0x08}, + { } +}; + + +static struct hda_verb vt1716S_uniwill_init_verbs[] = { + {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, + {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 6, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x13, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1716S_pcm_digital_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 3; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x25; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[3] = { "Front", "Surround", "C/LFE" }; + hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; + hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; + hda_nid_t nid, nid_vol, nid_mute; + int i, err; + + for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + nid_vol = nid_vols[i]; + nid_mute = nid_mutes[i]; + + if (i == AUTO_SEQ_CENLFE) { + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control( + spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x25; /* AOW3 */ + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1a: /* Mic */ + idx = 2; + break; + + case 0x1b: /* Line In */ + idx = 3; + break; + + case 0x1e: /* Front Mic */ + idx = 4; + break; + + case 0x1f: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x16); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx-1; + imux->num_items++; + } + return 0; +} + +static int vt1716S_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + spec->mixers[spec->num_mixers++] = via_smart51_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1716S_loopbacks[] = { + { 0x16, HDA_INPUT, 1 }, + { 0x16, HDA_INPUT, 2 }, + { 0x16, HDA_INPUT, 3 }, + { 0x16, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1716S(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1716S_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; + + spec->stream_name_analog = "VT1716S Analog"; + spec->stream_analog_playback = &vt1716S_pcm_analog_playback; + spec->stream_analog_capture = &vt1716S_pcm_analog_capture; + + spec->stream_name_digital = "VT1716S Digital"; + spec->stream_digital_playback = &vt1716S_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1716S_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x1a, 0, 3, 40); + override_mic_boost(codec, 0x1e, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; + spec->num_mixers++; + } + + spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; + spec->num_mixers++; + + spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1716S_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -4445,6 +5081,10 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1718S}, { .id = 0x11064441, .name = "VT1828S", .patch = patch_vt1718S}, + { .id = 0x11060433, .name = "VT1716S", + .patch = patch_vt1716S}, + { .id = 0x1106a721, .name = "VT1716S", + .patch = patch_vt1716S}, {} /* terminator */ }; -- cgit v1.2.2 From 25eaba2f8a6877ba6f58197c4723c2433a316e09 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:43 +0800 Subject: ALSA: HDA VIA: Add VT2002P support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 665 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 660 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2977004677ec..a94cc91c18ff 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -88,6 +88,7 @@ enum VIA_HDA_CODEC { VT1702, VT1718S, VT1716S, + VT2002P, CODEC_TYPES, }; @@ -184,6 +185,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1716S; else if (dev_id == 0x0441 || dev_id == 0x4441) codec_type = VT1718S; + else if (dev_id == 0x0438 || dev_id == 0x4438) + codec_type = VT2002P; else codec_type = UNKNOWN; return codec_type; @@ -193,11 +196,14 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) #define VIA_GPIO_EVENT 0x02 #define VIA_JACK_EVENT 0x04 #define VIA_MONO_EVENT 0x08 +#define VIA_SPEAKER_EVENT 0x10 +#define VIA_BIND_HP_EVENT 0x20 enum { VIA_CTL_WIDGET_VOL, VIA_CTL_WIDGET_MUTE, VIA_CTL_WIDGET_ANALOG_MUTE, + VIA_CTL_WIDGET_BIND_PIN_MUTE, }; enum { @@ -235,6 +241,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) flush_scheduled_work(); } + static int analog_input_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -262,13 +269,108 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, .put = analog_input_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } +static void via_hp_bind_automute(struct hda_codec *codec); + +static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int i; + int change = 0; + + long *valp = ucontrol->value.integer.value; + int lmute, rmute; + if (strstr(kcontrol->id.name, "Switch") == NULL) { + snd_printd("Invalid control!\n"); + return change; + } + change = snd_hda_mixer_amp_switch_put(kcontrol, + ucontrol); + /* Get mute value */ + lmute = *valp ? 0 : HDA_AMP_MUTE; + valp++; + rmute = *valp ? 0 : HDA_AMP_MUTE; + + /* Set hp pins */ + if (!spec->hp_independent_mode) { + for (i = 0; i < spec->autocfg.hp_outs; i++) { + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + snd_hda_codec_amp_update( + codec, spec->autocfg.hp_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } + + if (!lmute && !rmute) { + /* Line Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + /* unmute */ + via_hp_bind_automute(codec); + + } else { + if (lmute) { + /* Mute all left channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, + lmute); + } + if (rmute) { + /* mute all right channels */ + for (i = 1; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.line_out_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_update( + codec, + spec->autocfg.speaker_pins[i], + 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, + rmute); + } + } + return change; +} + +#define BIND_PIN_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = bind_pin_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + static struct snd_kcontrol_new vt1708_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, + BIND_PIN_MUTE, }; - static hda_nid_t vt1708_adc_nids[2] = { /* ADC1-2 */ 0x15, 0x27 @@ -304,6 +406,11 @@ static hda_nid_t vt1716S_adc_nids[2] = { 0x13, 0x14 }; +static hda_nid_t vt2002P_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -386,10 +493,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; hda_nid_t pin; + int i; - pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + for (i = 0; i < spec->autocfg.hp_outs; i++) { + pin = spec->autocfg.hp_pins[i]; + if (pin) /* connect to front */ + via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + } } static void via_auto_init_analog_input(struct hda_codec *codec) @@ -693,6 +803,107 @@ static void set_jack_power_state(struct hda_codec *codec) imux_is_smixer ? AC_PWRST_D0 : parm); snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, mono_out ? AC_PWRST_D0 : parm); + } else if (spec->codec_type == VT2002P) { + unsigned int present; + /* MUX9 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (26h), MW4 (1ch), MUX4(37h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x26, &parm); + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x37, + 0, AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Class-D */ + /* PW0 (24h), MW0(18h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* Mono Out */ + /* PW15 (31h), MW8(17h), MUX8(3bh) */ + present = snd_hda_codec_read( + codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else { + snd_hda_codec_write( + codec, 0x17, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + snd_hda_codec_write( + codec, 0x3b, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + } + + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } } @@ -760,6 +971,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, case VT1718S: nid = 0x34; break; + case VT2002P: + nid = 0x35; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -832,6 +1046,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ spec->multiout.num_dacs = 4; break; + case VT2002P: + nid = 0x35; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -848,7 +1065,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, if (spec->codec_type == VT1708S || spec->codec_type == VT1702 || spec->codec_type == VT1718S - || spec->codec_type == VT1716S) { + || spec->codec_type == VT1716S + || spec->codec_type == VT2002P) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -1088,6 +1306,11 @@ static int is_aa_path_mute(struct hda_codec *codec) start_idx = 1; end_idx = 3; break; + case VT2002P: + nid_mixer = 0x21; + start_idx = 0; + end_idx = 2; + break; default: return 0; } @@ -1146,6 +1369,10 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) verb = 0xf73; parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; + case VT2002P: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; default: return; /* other codecs are not supported */ } @@ -1645,6 +1872,66 @@ static void via_gpio_control(struct hda_codec *codec) } } +/* mute Internal-Speaker if HP is plugged */ +static void via_speaker_automute(struct hda_codec *codec) +{ + unsigned int hp_present; + struct via_spec *spec = codec->spec; + + if (spec->codec_type != VT2002P) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + struct snd_ctl_elem_id id; + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + /* notify change */ + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, "Speaker Playback Switch"); + snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, + &id); + } +} + +/* mute line-out and internal speaker if HP is plugged */ +static void via_hp_bind_automute(struct hda_codec *codec) +{ + unsigned int hp_present, present = 0; + struct via_spec *spec = codec->spec; + int i; + + if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) + return; + + hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + if (!spec->hp_independent_mode) { + /* Mute Line-Outs */ + for (i = 0; i < spec->autocfg.line_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.line_out_pins[i], + HDA_OUTPUT, 0, + HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); + if (hp_present) + present = hp_present; + } + /* Speakers */ + for (i = 0; i < spec->autocfg.speaker_outs; i++) + snd_hda_codec_amp_stereo( + codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + /* unsolicited event for jack sensing */ static void via_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1658,6 +1945,10 @@ static void via_unsol_event(struct hda_codec *codec, set_jack_power_state(codec); if (res & VIA_MONO_EVENT) via_mono_automute(codec); + if (res & VIA_SPEAKER_EVENT) + via_speaker_automute(codec); + if (res & VIA_BIND_HP_EVENT) + via_hp_bind_automute(codec); } static int via_init(struct hda_codec *codec) @@ -2067,10 +2358,19 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) /* init callback for auto-configuration model -- overriding the default init */ static int via_auto_init(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; + via_init(codec); via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); + if (spec->codec_type == VT2002P) { + via_hp_bind_automute(codec); + } else { + via_hp_automute(codec); + via_speaker_automute(codec); + } + return 0; } @@ -5001,6 +5301,359 @@ static int patch_vt1716S(struct hda_codec *codec) return 0; } + +/* for vt2002P */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt2002P_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .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 = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt2002P_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/8 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x37, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, + + /* set PW0 index=0 (MW0) */ + {0x24, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0x88}, + { } +}; + + +static struct hda_verb vt2002P_uniwill_init_verbs[] = { + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt2002P_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 3; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 4; + imux->num_items++; + + return 0; +} + +static int vt2002P_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt2002P_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt2002P */ +static int patch_vt2002P(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt2002P_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; + + spec->stream_name_analog = "VT2002P Analog"; + spec->stream_analog_playback = &vt2002P_pcm_analog_playback; + spec->stream_analog_capture = &vt2002P_pcm_analog_capture; + + spec->stream_name_digital = "VT2002P Digital"; + spec->stream_digital_playback = &vt2002P_pcm_digital_playback; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt2002P_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt2002P_loopbacks; +#endif + + return 0; +} /* * patch entries */ @@ -5085,6 +5738,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1716S}, { .id = 0x1106a721, .name = "VT1716S", .patch = patch_vt1716S}, + { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, {} /* terminator */ }; -- cgit v1.2.2 From ab6734e7ea32e9f9cbe0f55eeddf4aa629ed1c3d Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:46 +0800 Subject: ALSA: HDA VIA: Add VT1812 support. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 494 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 491 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a94cc91c18ff..b3c5e8a78154 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -89,6 +89,7 @@ enum VIA_HDA_CODEC { VT1718S, VT1716S, VT2002P, + VT1812, CODEC_TYPES, }; @@ -187,6 +188,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT1718S; else if (dev_id == 0x0438 || dev_id == 0x4438) codec_type = VT2002P; + else if (dev_id == 0x0448) + codec_type = VT1812; else codec_type = UNKNOWN; return codec_type; @@ -411,6 +414,12 @@ static hda_nid_t vt2002P_adc_nids[2] = { 0x10, 0x11 }; +static hda_nid_t vt1812_adc_nids[2] = { + /* ADC1-2 */ + 0x10, 0x11 +}; + + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -895,6 +904,120 @@ static void set_jack_power_state(struct hda_codec *codec) AC_VERB_SET_POWER_STATE, AC_PWRST_D0); } + /* MW9 (21h) */ + if (imux_is_smixer || !is_aa_path_mute(codec)) + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + else + snd_hda_codec_write( + codec, 0x21, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } else if (spec->codec_type == VT1812) { + unsigned int present; + /* MUX10 (1eh) = stereo mixer */ + imux_is_smixer = snd_hda_codec_read( + codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; + /* inputs */ + /* PW 5/6/7 (29h/2ah/2bh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x29, &parm); + set_pin_power_state(codec, 0x2a, &parm); + set_pin_power_state(codec, 0x2b, &parm); + if (imux_is_smixer) + parm = AC_PWRST_D0; + /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ + snd_hda_codec_write(codec, 0x1e, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x1f, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* outputs */ + /* AOW0 (8h)*/ + snd_hda_codec_write(codec, 0x8, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* PW4 (28h), MW4 (18h), MUX4(38h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x28, &parm); + snd_hda_codec_write(codec, 0x18, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x38, 0, + AC_VERB_SET_POWER_STATE, parm); + + /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x25, &parm); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_POWER_STATE, parm); + if (spec->hp_independent_mode) { + snd_hda_codec_write(codec, 0x9, 0, + AC_VERB_SET_POWER_STATE, parm); + } + + /* Internal Speaker */ + /* PW0 (24h), MW0(14h), MUX0(34h) */ + present = snd_hda_codec_read( + codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x24, &parm); + if (present) { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x34, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + /* Mono Out */ + /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ + present = snd_hda_codec_read( + codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x31, &parm); + if (present) { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + } else { + snd_hda_codec_write(codec, 0x1c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3c, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + snd_hda_codec_write(codec, 0x3e, 0, + AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + } + + /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ + parm = AC_PWRST_D3; + set_pin_power_state(codec, 0x33, &parm); + snd_hda_codec_write(codec, 0x1d, 0, + AC_VERB_SET_POWER_STATE, parm); + snd_hda_codec_write(codec, 0x3d, 0, + AC_VERB_SET_POWER_STATE, parm); + /* MW9 (21h) */ if (imux_is_smixer || !is_aa_path_mute(codec)) snd_hda_codec_write( @@ -974,6 +1097,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, case VT2002P: nid = 0x35; break; + case VT1812: + nid = 0x3d; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -1049,6 +1175,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, case VT2002P: nid = 0x35; break; + case VT1812: + nid = 0x3d; + break; default: nid = spec->autocfg.hp_pins[0]; break; @@ -1066,7 +1195,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, || spec->codec_type == VT1702 || spec->codec_type == VT1718S || spec->codec_type == VT1716S - || spec->codec_type == VT2002P) { + || spec->codec_type == VT2002P + || spec->codec_type == VT1812) { activate_ctl(codec, "Headphone Playback Volume", spec->hp_independent_mode); activate_ctl(codec, "Headphone Playback Switch", @@ -1307,6 +1437,7 @@ static int is_aa_path_mute(struct hda_codec *codec) end_idx = 3; break; case VT2002P: + case VT1812: nid_mixer = 0x21; start_idx = 0; end_idx = 2; @@ -1370,6 +1501,7 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ break; case VT2002P: + case VT1812: verb = 0xf93; parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ break; @@ -1878,7 +2010,7 @@ static void via_speaker_automute(struct hda_codec *codec) unsigned int hp_present; struct via_spec *spec = codec->spec; - if (spec->codec_type != VT2002P) + if (spec->codec_type != VT2002P && spec->codec_type != VT1812) return; hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, @@ -2364,7 +2496,7 @@ static int via_auto_init(struct hda_codec *codec) via_auto_init_multi_out(codec); via_auto_init_hp_out(codec); via_auto_init_analog_input(codec); - if (spec->codec_type == VT2002P) { + if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { via_hp_bind_automute(codec); } else { via_hp_automute(codec); @@ -5654,6 +5786,361 @@ static int patch_vt2002P(struct hda_codec *codec) return 0; } + +/* for vt1812 */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1812_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, + HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + .name = "Input Source", + .count = 2, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_verb vt1812_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* MUX Indices: Mic = 0 */ + {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, + + /* PW9 Output enable */ + {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, + + /* Enable Boost Volume backdoor */ + {0x1, 0xfb9, 0x24}, + + /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* set MUX0/1/4/13/15 = 0 (AOW0) */ + {0x34, AC_VERB_SET_CONNECT_SEL, 0}, + {0x35, AC_VERB_SET_CONNECT_SEL, 0}, + {0x38, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, + {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, + + /* Enable AOW0 to MW9 */ + {0x1, 0xfb8, 0xa8}, + { } +}; + + +static struct hda_verb vt1812_uniwill_init_verbs[] = { + {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, + {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, + {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, + { } +}; + +static struct hda_pcm_stream vt1812_pcm_analog_playback = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x8, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_pcm_open_close, + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup, + .close = via_pcm_open_close, + }, +}; + +static struct hda_pcm_stream vt1812_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1812_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = spec->private_dac_nids; + if (cfg->line_out_pins[0]) + spec->multiout.dac_nids[0] = 0x8; + return 0; +} + + +/* add playback controls from the parsed DAC table */ +static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + if (!cfg->line_out_pins[0]) + return -1; + + /* Line-Out: PortE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = 0x9; + spec->hp_independent_mode_index = 1; + + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL( + spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + create_hp_imux(spec); + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux[0]; + int i, err, idx = 0; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x2b: /* Mic */ + idx = 0; + break; + + case 0x2a: /* Line In */ + idx = 1; + break; + + case 0x29: /* Front Mic */ + idx = 2; + break; + } + err = via_new_analog_input(spec, labels[i], idx, 0x21); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + /* build volume/mute control of loopback */ + err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); + if (err < 0) + return err; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = 5; + imux->num_items++; + + /* for digital mic select */ + imux->items[imux->num_items].label = "Digital Mic"; + imux->items[imux->num_items].index = 6; + imux->num_items++; + + return 0; +} + +static int vt1812_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + fill_dig_outs(codec); + err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + + if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) + return 0; /* can't find valid BIOS pin config */ + + err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->input_mux = &spec->private_imux[0]; + + if (spec->hp_mux) + spec->mixers[spec->num_mixers++] = via_hp_mixer; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1812_loopbacks[] = { + { 0x21, HDA_INPUT, 0 }, + { 0x21, HDA_INPUT, 1 }, + { 0x21, HDA_INPUT, 2 }, + { } /* end */ +}; +#endif + + +/* patch for vt1812 */ +static int patch_vt1812(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1812_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + + spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; + spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; + + spec->stream_name_analog = "VT1812 Analog"; + spec->stream_analog_playback = &vt1812_pcm_analog_playback; + spec->stream_analog_capture = &vt1812_pcm_analog_capture; + + spec->stream_name_digital = "VT1812 Digital"; + spec->stream_digital_playback = &vt1812_pcm_digital_playback; + + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1812_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); + get_mux_nids(codec); + override_mic_boost(codec, 0x2b, 0, 3, 40); + override_mic_boost(codec, 0x29, 0, 3, 40); + spec->mixers[spec->num_mixers] = vt1812_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; + codec->patch_ops.unsol_event = via_unsol_event, + +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1812_loopbacks; +#endif + + return 0; +} + /* * patch entries */ @@ -5740,6 +6227,7 @@ static struct hda_codec_preset snd_hda_preset_via[] = { .patch = patch_vt1716S}, { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, + { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, {} /* terminator */ }; -- cgit v1.2.2 From 71eb7dccb7d2d22236dbe46db07f8000d09fba01 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:49 +0800 Subject: ALSA: HDA VIA: rename vt1708_control_templates[]. To via_control_templates[]. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b3c5e8a78154..257b51c61422 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -367,7 +367,7 @@ static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, .put = bind_pin_switch_put, \ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } -static struct snd_kcontrol_new vt1708_control_templates[] = { +static struct snd_kcontrol_new via_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), ANALOG_INPUT_MUTE, @@ -430,7 +430,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, knew = snd_array_new(&spec->kctls); if (!knew) return -ENOMEM; - *knew = vt1708_control_templates[type]; + *knew = via_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; -- cgit v1.2.2 From bfdc675a73f7697ead12c07dbf11e2b2632676f4 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:50 +0800 Subject: ALSA: HDA VIA: Change PW4 connect select default to to MW0. According to customer request, hp should be default to redirected mode, i.e. PW4 connect select default to to MW0. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 257b51c61422..4ea18a759a05 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1541,8 +1541,8 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Setup default input to PW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Setup default input MW0 to PW4 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -2668,8 +2668,8 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Set input of PW4 as AOW4 */ - {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Set input of PW4 as MW0 */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -3222,7 +3222,7 @@ static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* Setup default input to PW4 */ - {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PW10 Input enable */ -- cgit v1.2.2 From 8e86597f3cbd0a58808560116abe1bc0023256b0 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:52 +0800 Subject: ALSA: HDA VIA: comments: update copyright, changeset, etc. Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4ea18a759a05..fab875a21726 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1,10 +1,10 @@ /* * Universal Interface for Intel High Definition Audio Codec * - * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec + * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec * - * Copyright (c) 2006-2008 Lydia Wang - * Takashi Iwai + * (C) 2006-2009 VIA Technology, Inc. + * (C) 2006-2008 Takashi Iwai * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,11 @@ /* 2008-04-09 Lydia Wang Add Independent HP feature */ /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ +/* 2009-02-16 Logan Li Add support for VT1718S */ +/* 2009-03-13 Logan Li Add support for VT1716S */ +/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ +/* 2009-07-08 Lydia Wang Add support for VT2002P */ +/* 2009-07-21 Lydia Wang Add support for VT1812 */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -- cgit v1.2.2 From 377ff31ae06f0d2644839246cd18c3e17fe62a48 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Sat, 10 Oct 2009 19:08:55 +0800 Subject: ALSA: HDA VIA: Only cosmetic changes Signed-off-by: Lydia Wang Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 64 ++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fab875a21726..30260e259181 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -22,26 +22,26 @@ */ /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ -/* */ +/* */ /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ -/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ -/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ +/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ +/* 2006-08-02 Lydia Wang Add support to VT1709 codec */ /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ -/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ -/* 2007-09-17 Lydia Wang Add VT1708B codec support */ +/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ +/* 2007-09-17 Lydia Wang Add VT1708B codec support */ /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ -/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ -/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ -/* 2008-04-09 Lydia Wang Add Independent HP feature */ +/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ +/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ +/* 2008-04-09 Lydia Wang Add Independent HP feature */ /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ -/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ +/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ /* 2009-02-16 Logan Li Add support for VT1718S */ /* 2009-03-13 Logan Li Add support for VT1716S */ /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ /* 2009-07-08 Lydia Wang Add support for VT2002P */ /* 2009-07-21 Lydia Wang Add support for VT1812 */ -/* */ +/* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -486,7 +486,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } @@ -1545,7 +1545,7 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - + /* Setup default input MW0 to PW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, /* PW9 Output enable */ @@ -1865,8 +1865,10 @@ static int via_build_pcms(struct hda_codec *codec) codec->pcm_info = info; info->name = spec->stream_name_analog; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback); - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *(spec->stream_analog_playback); + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; @@ -2116,7 +2118,7 @@ static int via_init(struct hda_codec *codec) if (spec->slave_dig_outs[0]) codec->slave_dig_outs = spec->slave_dig_outs; - return 0; + return 0; } #ifdef SND_HDA_NEEDS_RESUME @@ -2161,8 +2163,8 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.num_dacs = cfg->line_outs; spec->multiout.dac_nids = spec->private_dac_nids; - - for(i = 0; i < 4; i++) { + + for (i = 0; i < 4; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ @@ -2200,7 +2202,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (!nid) continue; - + nid_vol = nid_vols[i]; if (i == AUTO_SEQ_CENLFE) { @@ -2229,7 +2231,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ + } else if (i == AUTO_SEQ_FRONT) { /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", @@ -2243,7 +2245,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -2343,7 +2345,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -2576,7 +2578,7 @@ static int patch_vt1708(struct hda_codec *codec) "from BIOS. Using genenic mode...\n"); } - + spec->stream_name_analog = "VT1708 Analog"; spec->stream_analog_playback = &vt1708_pcm_analog_playback; /* disable 32bit format on VT1708 */ @@ -2588,7 +2590,7 @@ static int patch_vt1708(struct hda_codec *codec) spec->stream_digital_playback = &vt1708_pcm_digital_playback; spec->stream_digital_capture = &vt1708_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1708_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); @@ -2775,11 +2777,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec, spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ } else if (cfg->line_outs == 3) { /* 6 channels */ - for(i = 0; i < cfg->line_outs; i++) { + for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; if (nid) { /* config dac list */ - switch(i) { + switch (i) { case AUTO_SEQ_FRONT: /* AOW0 */ spec->multiout.dac_nids[i] = 0x10; @@ -2814,7 +2816,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, for (i = 0; i <= AUTO_SEQ_SIDE; i++) { nid = cfg->line_out_pins[i]; - if (!nid) + if (!nid) continue; nid_vol = nid_vols[i]; @@ -2845,7 +2847,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, HDA_OUTPUT)); if (err < 0) return err; - } else if (i == AUTO_SEQ_FRONT){ + } else if (i == AUTO_SEQ_FRONT) { /* ADD control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", @@ -2859,7 +2861,7 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, HDA_INPUT)); if (err < 0) return err; - + /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, @@ -2955,7 +2957,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, case 0x1d: /* Mic */ idx = 2; break; - + case 0x1e: /* Line In */ idx = 3; break; @@ -3064,7 +3066,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); @@ -3158,7 +3160,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec) spec->stream_digital_playback = &vt1709_pcm_digital_playback; spec->stream_digital_capture = &vt1709_pcm_digital_capture; - + if (!spec->adc_nids && spec->input_mux) { spec->adc_nids = vt1709_adc_nids; spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); -- cgit v1.2.2 From 0f48327eac5f65ad029d7112cac97577766730ba Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 12 Oct 2009 15:56:17 +1100 Subject: sound: use semicolons to end statements Fixes: sound/pci/hda/patch_via.c: In function 'patch_vt1718S': sound/pci/hda/patch_via.c:4951: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt1716S': sound/pci/hda/patch_via.c:5441: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt2002P': sound/pci/hda/patch_via.c:5794: error: expected expression before 'return' sound/pci/hda/patch_via.c: In function 'patch_vt1812': sound/pci/hda/patch_via.c:6148: error: expected expression before 'return' Signed-off-by: Stephen Rothwell Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 30260e259181..a294060ed684 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -4942,7 +4942,7 @@ static int patch_vt1718S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1718S_loopbacks; @@ -5432,7 +5432,7 @@ static int patch_vt1716S(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1716S_loopbacks; @@ -5785,7 +5785,7 @@ static int patch_vt2002P(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt2002P_loopbacks; @@ -6139,7 +6139,7 @@ static int patch_vt1812(struct hda_codec *codec) codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; - codec->patch_ops.unsol_event = via_unsol_event, + codec->patch_ops.unsol_event = via_unsol_event; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1812_loopbacks; -- cgit v1.2.2 From 68f139204c1a2b10cc292d9cca036ebdbb6730a8 Mon Sep 17 00:00:00 2001 From: Wu Zhangjin Date: Sat, 10 Oct 2009 23:53:49 +0800 Subject: ALSA: SND_CS5535AUDIO: Remove the X86 platform dependency SND_CS5535AUDIO is available on Loongson(MIPS compatible) family machines, and checked it with ARCH=x86_64, no relative compiling warnings & errors, so, remove the platform dependency directly. Reported-by: rixed@happyleptic.org Acked-by: Andres Salomon Signed-off-by: Wu Zhangjin Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index fb5ee3cc3968..75c602b5b132 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -259,7 +259,6 @@ config SND_CS5530 config SND_CS5535AUDIO tristate "CS5535/CS5536 Audio" - depends on X86 && !X86_64 select SND_PCM select SND_AC97_CODEC help -- cgit v1.2.2 From d2ed82a3e7d1f63b2da3f1aa5763667dd17919ac Mon Sep 17 00:00:00 2001 From: Logan Li Date: Wed, 14 Oct 2009 10:10:38 +0800 Subject: ALSA: HDA VIA: Remove 48k sample rate limit for S/PDIF 48 kHz limit is for slightly better stability, and sample rates other than 48k (like 96k/192k) are for better sound quality. We choose better quality, so remove the 48k limit. Signed-off-by: Logan Li Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a294060ed684..89e084d45369 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -4626,7 +4626,6 @@ static struct hda_pcm_stream vt1718S_pcm_digital_playback = { .substreams = 2, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5124,7 +5123,6 @@ static struct hda_pcm_stream vt1716S_pcm_digital_playback = { .substreams = 2, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5561,7 +5559,6 @@ static struct hda_pcm_stream vt2002P_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, @@ -5914,7 +5911,6 @@ static struct hda_pcm_stream vt1812_pcm_digital_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, /* NID is set in via_build_pcms */ .ops = { .open = via_dig_playback_pcm_open, -- cgit v1.2.2 From 739b47f1e5aa3b36eadd7906cc6b41f0175c6ed1 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:34:19 +0100 Subject: ALSA: hda - select IbexPeak handler for Calpella An earlier patch merely adds id for 0x80862804. It has 2/3 cvt/pin nodes and shall be tied to the IbexPeak handler. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 01a18ed475ac..7c23016fe8fa 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -684,7 +684,7 @@ static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ -- cgit v1.2.2 From f5d6def5c642587434c42722c57fb65642f61038 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:38:26 +0100 Subject: ALSA: hda - vectorize get_empty_pcm_device() This unifies the code and data structure, and makes it easy to add more HDMI devices. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 49 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index af989f660cca..49289cd50697 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2885,43 +2885,26 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) 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 + /* audio device indices; not linear to keep compatibility */ + static int audio_idx[HDA_PCM_NTYPES][5] = { + [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, + [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; - int i, dev; - - switch (type) { - case HDA_PCM_TYPE_AUDIO: - for (i = 0; i < ARRAY_SIZE(audio_idx); i++) { - dev = audio_idx[i]; - if (!test_bit(dev, bus->pcm_dev_bits)) - goto ok; - } - snd_printk(KERN_WARNING "Too many audio devices\n"); - return -EAGAIN; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - dev = dev_idx[type]; - if (test_bit(dev, bus->pcm_dev_bits)) { - snd_printk(KERN_WARNING "%s already defined\n", - dev_name[type]); - return -EAGAIN; - } - break; - default: + int i; + + if (type >= HDA_PCM_NTYPES) { snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); return -EINVAL; } - ok: - set_bit(dev, bus->pcm_dev_bits); - return dev; + + for (i = 0; audio_idx[type][i] >= 0 ; i++) + if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) + return audio_idx[type][i]; + + snd_printk(KERN_WARNING "Too many %s devices\n", dev_name[type]); + return -EAGAIN; } /* -- cgit v1.2.2 From 92608badc519a8c1f65d93743396517aaa582b53 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:40:03 +0100 Subject: ALSA: hda - allow up to 4 HDMI devices The new Intel HDMI codec supports 2 independant HDMI/DisplayPort pipes. We'll be exporting them as 2 pcm devices. So bump up the allowed number of HDMI devices. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 49289cd50697..2c1366343335 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2889,7 +2889,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, - [HDA_PCM_TYPE_HDMI] = { 3, -1 }, + [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 }, [HDA_PCM_TYPE_MODEM] = { 6, -1 }, }; int i; -- cgit v1.2.2 From 6797cf2bfcbf2fa1fd05c0b785dc1402f73e2ce5 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:40:40 +0100 Subject: ALSA: hda - convert intelhdmi global references to local parameters No behavior change. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 80 ++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 37 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 7c23016fe8fa..2dfb1efc2d08 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -189,35 +189,36 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { */ #ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int *packet_index, int *byte_index) { int val; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0); + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); *packet_index = val >> 5; *byte_index = val & 0x1f; } #endif -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, int packet_index, int byte_index) { int val; val = (packet_index << 5) | (byte_index & 0x1f); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); } -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char val) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); } -static void hdmi_enable_output(struct hda_codec *codec) +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) { /* Unmute */ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) @@ -231,7 +232,8 @@ static void hdmi_enable_output(struct hda_codec *codec) /* * Enable Audio InfoFrame Transmission */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec) +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, @@ -241,37 +243,40 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec) /* * Disable Audio InfoFrame Transmission */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec) +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) { hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); } -static int hdmi_get_channel_count(struct hda_codec *codec) +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, + return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } -static void hdmi_set_channel_count(struct hda_codec *codec, int chs) +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); - if (chs != hdmi_get_channel_count(codec)) +#ifdef CONFIG_SND_DEBUG_VERBOSE + if (chs != hdmi_get_channel_count(codec, nid)) snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec)); + chs, hdmi_get_channel_count(codec, nid)); +#endif } -static void hdmi_debug_channel_mapping(struct hda_codec *codec) +static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; int slot; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, cvt_nid, 0, + slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0x7); @@ -293,7 +298,7 @@ static void hdmi_parse_eld(struct hda_codec *codec) * Audio InfoFrame routines */ -static void hdmi_debug_dip_size(struct hda_codec *codec) +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; @@ -310,7 +315,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec) #endif } -static void hdmi_clear_dip_buffers(struct hda_codec *codec) +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef BE_PARANOID int i, j; @@ -340,14 +345,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec) } static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) { u8 *params = (u8 *)ai; u8 sum = 0; int i; - hdmi_debug_dip_size(codec); - hdmi_clear_dip_buffers(codec); /* be paranoid */ + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ for (i = 0; i < sizeof(ai); i++) sum += params[i]; @@ -386,7 +392,7 @@ static void init_channel_allocations(void) * * TODO: it could select the wrong CA from multiple candidates. */ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; @@ -439,8 +445,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, return ai->CA; } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - struct hdmi_audio_infoframe *ai) +static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) { int i; @@ -453,15 +459,15 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, */ for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, cvt_nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); - hdmi_debug_channel_mapping(codec); + hdmi_debug_channel_mapping(codec, nid); } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { struct hdmi_audio_infoframe ai = { @@ -471,11 +477,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, .CC02_CT47 = substream->runtime->channels - 1, }; - hdmi_setup_channel_allocation(codec, &ai); - hdmi_setup_channel_mapping(codec, &ai); + hdmi_setup_channel_allocation(codec, nid, &ai); + hdmi_setup_channel_mapping(codec, nid, &ai); - hdmi_fill_audio_infoframe(codec, &ai); - hdmi_start_infoframe_trans(codec); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); } @@ -553,7 +559,7 @@ static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, { struct intel_hdmi_spec *spec = codec->spec; - hdmi_stop_infoframe_trans(codec); + hdmi_stop_infoframe_trans(codec, pin_nid); return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -569,9 +575,9 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream); - hdmi_set_channel_count(codec, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, substream); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream); return 0; } @@ -619,7 +625,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec); + hdmi_enable_output(codec, pin_nid); snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, -- cgit v1.2.2 From 7bedb011ef4db93b15049ece8d50b29d6fe6af9d Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:41:44 +0100 Subject: ALSA: hda - remove intelhdmi dependency on multiout We'll be managing multiple HDMI audio sources/sinks on our own. So remove multiout dependency from intelhdmi. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 2dfb1efc2d08..02be428be667 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -39,7 +39,6 @@ static hda_nid_t pin_nid; /* HDMI output pin */ #define INTEL_HDMI_EVENT_TAG 0x08 struct intel_hdmi_spec { - struct hda_multi_out multiout; struct hda_pcm pcm_rec; struct hdmi_eld sink_eld; }; @@ -548,9 +547,7 @@ static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - return snd_hda_multi_out_dig_open(codec, &spec->multiout); + return 0; } static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, @@ -561,7 +558,8 @@ static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, hdmi_stop_infoframe_trans(codec, pin_nid); - return snd_hda_multi_out_dig_close(codec, &spec->multiout); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + return 0; } static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -570,15 +568,12 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - - snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); - - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels); hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; } @@ -616,7 +611,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) struct intel_hdmi_spec *spec = codec->spec; int err; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); if (err < 0) return err; @@ -657,10 +652,6 @@ static int do_patch_intel_hdmi(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->multiout.num_dacs = 0; /* no analog */ - spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = cvt_nid; - codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops; -- cgit v1.2.2 From 70ca35fb42680fc4315d4a01f6c77c9a9962199c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:42:18 +0100 Subject: ALSA: hda - use pcm prepare/cleanup callbacks for intelhdmi Remove pcm callbacks open/close in favor of the prepare/cleanup. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 02be428be667..c17feacab754 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -543,16 +543,9 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - return 0; -} - -static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; @@ -582,9 +575,8 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { .channels_min = 2, .channels_max = 8, .ops = { - .open = intel_hdmi_playback_pcm_open, - .close = intel_hdmi_playback_pcm_close, - .prepare = intel_hdmi_playback_pcm_prepare + .prepare = intel_hdmi_playback_pcm_prepare, + .cleanup = intel_hdmi_playback_pcm_cleanup, }, }; -- cgit v1.2.2 From ddb8152b054e357907f57fb5ae65d494a3c79065 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:43:03 +0100 Subject: ALSA: hda - reorder intelhdmi prepare/cleanup callbacks No behavior change. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index c17feacab754..6be5ca44a83b 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -543,30 +543,30 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ -static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; + hdmi_set_channel_count(codec, cvt_nid, + substream->runtime->channels); - hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_setup_audio_infoframe(codec, cvt_nid, substream); - snd_hda_codec_cleanup_stream(codec, hinfo->nid); + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; } -static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, - substream->runtime->channels); + struct intel_hdmi_spec *spec = codec->spec; - hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_stop_infoframe_trans(codec, pin_nid); - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v1.2.2 From 54a25f87e943fc77f57e86849897ad6602519286 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:44:26 +0100 Subject: ALSA: hda - vectorize intelhdmi The Intel IbexPeak HDMI codec supports 2 converters and 3 pins, which requires converting the cvt_nid/pin_nid to arrays. The active pin number (the one connected with a live HDMI monitor/sink) will be dynamically identified on hotplug events. It exports two HDMI devices, so that user space can choose the A/V pipe for sending the audio samples. It's still undefined behavior when there are two active monitors connected and routed to the same audio converter. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_eld.c | 5 +- sound/pci/hda/hda_local.h | 6 +- sound/pci/hda/patch_intelhdmi.c | 191 +++++++++++++++++++++++++++++++--------- 3 files changed, 155 insertions(+), 47 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 9446a5abea13..20fa6aee29c0 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -560,13 +560,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, } -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index) { char name[32]; struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "eld#%d", codec->addr); + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); err = snd_card_proc_new(codec->bus->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5f1dcc59002b..461e0c15c77a 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -541,11 +541,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t); void snd_hdmi_show_eld(struct hdmi_eld *eld); #ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld); +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, + int index); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); #else static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld) + struct hdmi_eld *eld, + int index) { return 0; } diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 6be5ca44a83b..08ea88deba6f 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -33,14 +33,43 @@ #include "hda_codec.h" #include "hda_local.h" -static hda_nid_t cvt_nid; /* audio converter */ -static hda_nid_t pin_nid; /* HDMI output pin */ +/* + * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device + * could support two independent pipes, each of them can be connected to one or + * more ports (DVI, HDMI or DisplayPort). + * + * The HDA correspondence of pipes/ports are converter/pin nodes. + */ +#define INTEL_HDMI_CVTS 2 +#define INTEL_HDMI_PINS 3 -#define INTEL_HDMI_EVENT_TAG 0x08 +static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = { + "INTEL HDMI 0", + "INTEL HDMI 1", +}; struct intel_hdmi_spec { - struct hda_pcm pcm_rec; - struct hdmi_eld sink_eld; + int num_cvts; + int num_pins; + hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */ + + /* + * source connection for each pin + */ + hda_nid_t pin_cvt[INTEL_HDMI_PINS+1]; + + /* + * HDMI sink attached to each pin + */ + bool sink_present[INTEL_HDMI_PINS]; + bool sink_eldv[INTEL_HDMI_PINS]; + struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; + + /* + * export one pcm per pipe + */ + struct hda_pcm pcm_rec[INTEL_HDMI_CVTS]; }; struct hdmi_audio_infoframe { @@ -183,6 +212,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, }; + +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + /* * HDMI routines */ @@ -283,12 +325,12 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) #endif } -static void hdmi_parse_eld(struct hda_codec *codec) +static void hdmi_parse_eld(struct hda_codec *codec, int index) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld = &spec->sink_eld[index]; - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) snd_hdmi_show_eld(eld); } @@ -395,7 +437,7 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, struct hdmi_audio_infoframe *ai) { struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld; + struct hdmi_eld *eld; int i; int spk_mask = 0; int channels = 1 + (ai->CC02_CT47 & 0x7); @@ -407,6 +449,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, if (channels <= 2) return 0; + i = hda_node_index(spec->pin_cvt, nid); + if (i < 0) + return 0; + eld = &spec->sink_eld[i]; + /* * HDMI sink's ELD info cannot always be retrieved for now, e.g. * in console or for audio devices. Assume the highest speakers @@ -469,6 +516,9 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) { + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; struct hdmi_audio_infoframe ai = { .type = 0x84, .ver = 0x01, @@ -479,8 +529,16 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, hdmi_setup_channel_allocation(codec, nid, &ai); hdmi_setup_channel_mapping(codec, nid, &ai); - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (spec->sink_present[i] != true) + continue; + + pin_nid = spec->pin[i]; + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } } @@ -490,27 +548,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int pind = !!(res & AC_UNSOL_RES_PD); int eldv = !!(res & AC_UNSOL_RES_ELDV); + int index; printk(KERN_INFO - "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", - pind, eldv); + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_present[index] = pind; + spec->sink_eldv[index] = eldv; if (pind && eldv) { - hdmi_parse_eld(codec); + hdmi_parse_eld(codec, index); /* TODO: do real things about ELD */ } } static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) { + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); printk(KERN_INFO - "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, subtag, cp_state, cp_ready); @@ -525,10 +595,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) { + struct intel_hdmi_spec *spec = codec->spec; int tag = res >> AC_UNSOL_RES_TAG_SHIFT; int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - if (tag != INTEL_HDMI_EVENT_TAG) { + if (hda_node_index(spec->pin, tag) < 0) { snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); return; } @@ -549,10 +620,10 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - hdmi_set_channel_count(codec, cvt_nid, + hdmi_set_channel_count(codec, hinfo->nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, cvt_nid, substream); + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); return 0; @@ -563,8 +634,14 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != hinfo->nid) + continue; - hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_stop_infoframe_trans(codec, spec->pin[i]); + } snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; @@ -583,17 +660,19 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { static int intel_hdmi_build_pcms(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; + int i; - codec->num_pcms = 1; + codec->num_pcms = spec->num_cvts; codec->pcm_info = info; - /* NID to query formats and rates and setup streams */ - intel_hdmi_pcm_playback.nid = cvt_nid; - - info->name = "INTEL HDMI"; - info->pcm_type = HDA_PCM_TYPE_HDMI; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; + for (i = 0; i < codec->num_pcms; i++, info++) { + info->name = intel_hdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + intel_hdmi_pcm_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + } return 0; } @@ -602,29 +681,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; int err; + int i; - err = snd_hda_create_spdif_out_ctls(codec, cvt_nid); - if (err < 0) - return err; + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]); + if (err < 0) + return err; + } return 0; } static int intel_hdmi_init(struct hda_codec *codec) { - hdmi_enable_output(codec, pin_nid); + struct intel_hdmi_spec *spec = codec->spec; + int i; - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | INTEL_HDMI_EVENT_TAG); + for (i = 0; spec->pin[i]; i++) { + hdmi_enable_output(codec, spec->pin[i]); + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | spec->pin[i]); + } return 0; } static void intel_hdmi_free(struct hda_codec *codec) { struct intel_hdmi_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); - snd_hda_eld_proc_free(codec, &spec->sink_eld); kfree(spec); } @@ -636,18 +725,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static int do_patch_intel_hdmi(struct hda_codec *codec) +static struct intel_hdmi_spec static_specs[] = { + { + .num_cvts = 1, + .num_pins = 1, + .cvt = { 0x2 }, + .pin = { 0x3 }, + .pin_cvt = { 0x2 }, + }, + { + .num_cvts = 2, + .num_pins = 3, + .cvt = { 0x2, 0x3 }, + .pin = { 0x4, 0x5, 0x6 }, + .pin_cvt = { 0x2, 0x2, 0x2 }, + }, +}; + +static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) { struct intel_hdmi_spec *spec; + int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; + *spec = static_specs[spec_id]; codec->spec = spec; codec->patch_ops = intel_hdmi_patch_ops; - snd_hda_eld_proc_new(codec, &spec->sink_eld); + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); init_channel_allocations(); @@ -656,16 +765,12 @@ static int do_patch_intel_hdmi(struct hda_codec *codec) static int patch_intel_hdmi(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x03; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 0); } static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) { - cvt_nid = 0x02; - pin_nid = 0x04; - return do_patch_intel_hdmi(codec); + return do_patch_intel_hdmi(codec, 1); } static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { -- cgit v1.2.2 From 69fb346896b4265c0cbcbd2fdd1a97ae09fe198d Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:45:04 +0100 Subject: ALSA: hda - get intelhdmi max channels from widget caps Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 08ea88deba6f..3c68aa9742d7 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -650,7 +650,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, static struct hda_pcm_stream intel_hdmi_pcm_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 8, .ops = { .prepare = intel_hdmi_playback_pcm_prepare, .cleanup = intel_hdmi_playback_pcm_cleanup, @@ -667,11 +666,17 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec) codec->pcm_info = info; for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + info->name = intel_hdmi_pcm_names[i]; info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; } return 0; -- cgit v1.2.2 From f424367c3a393ca8b9669ceaa5b7f959d83bb14c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:45:35 +0100 Subject: ALSA: hda - auto parse intelhdmi cvt/pin configurations Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 3c68aa9742d7..1c374f11ed07 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -213,6 +213,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { }; +/* + * HDA/HDMI auto parsing + */ + static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) { int i; @@ -225,6 +229,113 @@ static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) return -EINVAL; } +static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, + HDA_MAX_CONNECTIONS); + if (conn_len > 1) + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + else + curr = 0; + + index = hda_node_index(spec->pin, pin_nid); + if (index < 0) + return -EINVAL; + + spec->pin_cvt[index] = conn_list[curr]; + + return 0; +} + +static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_pins >= INTEL_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d \n", pin_nid); + return -EINVAL; + } + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return intel_hdmi_read_pin_conn(codec, pin_nid); +} + +static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct intel_hdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= INTEL_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d \n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + +static int intel_hdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (intel_hdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & AC_PINCAP_HDMI)) + continue; + if (intel_hdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + return 0; +} + /* * HDMI routines */ @@ -756,8 +867,15 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) if (spec == NULL) return -ENOMEM; - *spec = static_specs[spec_id]; codec->spec = spec; + if (intel_hdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } + if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) + snd_printk(KERN_WARNING + "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops; for (i = 0; i < spec->num_pins; i++) -- cgit v1.2.2 From fd080b2d8a6a13992b4b1b6300e1befdb9e089f2 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 30 Oct 2009 11:46:22 +0100 Subject: ALSA: hda - remove static intelhdmi configurations Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 1c374f11ed07..650de1b4ea8d 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -841,24 +841,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .unsol_event = intel_hdmi_unsol_event, }; -static struct intel_hdmi_spec static_specs[] = { - { - .num_cvts = 1, - .num_pins = 1, - .cvt = { 0x2 }, - .pin = { 0x3 }, - .pin_cvt = { 0x2 }, - }, - { - .num_cvts = 2, - .num_pins = 3, - .cvt = { 0x2, 0x3 }, - .pin = { 0x4, 0x5, 0x6 }, - .pin_cvt = { 0x2, 0x2, 0x2 }, - }, -}; - -static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) +static int patch_intel_hdmi(struct hda_codec *codec) { struct intel_hdmi_spec *spec; int i; @@ -873,9 +856,6 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) kfree(spec); return -EINVAL; } - if (memcmp(spec, static_specs + spec_id, sizeof(*spec))) - snd_printk(KERN_WARNING - "HDMI: auto parse disagree with known config\n"); codec->patch_ops = intel_hdmi_patch_ops; for (i = 0; i < spec->num_pins; i++) @@ -886,23 +866,13 @@ static int do_patch_intel_hdmi(struct hda_codec *codec, int spec_id) return 0; } -static int patch_intel_hdmi(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 0); -} - -static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec) -{ - return do_patch_intel_hdmi(codec, 1); -} - static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, - { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak }, + { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, + { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ }; -- cgit v1.2.2 From 36dd5c4afff825fca1b6ccde678f51d6933a6850 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 20 Oct 2009 13:18:04 +0800 Subject: ALSA: VIA HDA: Add support for VT1818S. Add support for VT1818S codec, which is similiar with VT1708S. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 89e084d45369..5ec0e39593b5 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -41,6 +41,7 @@ /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ /* 2009-07-08 Lydia Wang Add support for VT2002P */ /* 2009-07-21 Lydia Wang Add support for VT1812 */ +/* 2009-09-19 Lydia Wang Add support for VT1818S */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -195,6 +196,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) codec_type = VT2002P; else if (dev_id == 0x0448) codec_type = VT1812; + else if (dev_id == 0x0440) + codec_type = VT1708S; else codec_type = UNKNOWN; return codec_type; @@ -4130,11 +4133,17 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; - spec->stream_name_analog = "VT1708S Analog"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_analog = "VT1818S Analog"; + else + spec->stream_name_analog = "VT1708S Analog"; spec->stream_analog_playback = &vt1708S_pcm_analog_playback; spec->stream_analog_capture = &vt1708S_pcm_analog_capture; - spec->stream_name_digital = "VT1708S Digital"; + if (codec->vendor_id == 0x11060440) + spec->stream_name_digital = "VT1818S Digital"; + else + spec->stream_name_digital = "VT1708S Digital"; spec->stream_digital_playback = &vt1708S_pcm_digital_playback; if (!spec->adc_nids && spec->input_mux) { @@ -6231,6 +6240,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, + { .id = 0x11060440, .name = "VT1818S", + .patch = patch_vt1708S}, {} /* terminator */ }; -- cgit v1.2.2 From 84ed1a1942e8c28fb4c23a6235ec48672fc43e49 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 23 Oct 2009 16:03:08 +0200 Subject: ALSA: Cleanup redundant tests on unsigned The variables are unsigned so the test `>= 0' is always true, the `< 0' test always fails. In these cases the other part of the test catches wrapped values. In dac_audio_write() there does not occur a test for wrapped values, but the test appears redundant. Signed-off-by: Roel Kluin Signed-off-by: Takashi Iwai --- sound/pci/ca0106/ca0106_proc.c | 4 ++-- sound/pci/ctxfi/ctatc.c | 2 +- sound/pci/emu10k1/emu10k1x.c | 3 +-- sound/pci/emu10k1/emuproc.c | 4 ++-- sound/pci/emu10k1/io.c | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index c62b7d10ec61..15523e60351c 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -304,7 +304,7 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) { + if (reg < 0x40 && val <= 0xffffffff) { spin_lock_irqsave(&emu->emu_lock, flags); outl(val, emu->port + (reg & 0xfffffffc)); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -405,7 +405,7 @@ static void snd_ca0106_proc_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) ) + if (reg < 0x80 && val <= 0xffffffff && channel_id <= 3) snd_ca0106_ptr_write(emu, reg, channel_id, val); } } diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index b1b3a644f738..6bfce99b42a2 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -240,7 +240,7 @@ static int select_rom(unsigned int pitch) } else if (pitch == 0x02000000) { /* pitch == 2 */ return 3; - } else if (pitch >= 0x0 && pitch <= 0x08000000) { + } else if (pitch <= 0x08000000) { /* 0 <= pitch <= 8 */ return 0; } else { diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 36e08bd2b3cc..6b8ae7b5cd54 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1040,8 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0x49) && (reg >= 0) && (val <= 0xffffffff) - && (channel_id >= 0) && (channel_id <= 2) ) + if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2) snd_emu10k1x_ptr_write(emu, reg, channel_id, val); } } diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 216f9748aff5..baa7cd508cd8 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -451,7 +451,7 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - if ((reg < 0x40) && (reg >= 0) && (val <= 0xffffffff) ) { + if (reg < 0x40 && val <= 0xffffffff) { spin_lock_irqsave(&emu->emu_lock, flags); outl(val, emu->port + (reg & 0xfffffffc)); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -527,7 +527,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry, while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if ((reg < 0xa0) && (reg >= 0) && (val <= 0xffffffff) && (channel_id >= 0) && (channel_id <= 3) ) + if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3) snd_ptr_write(emu, iobase, reg, channel_id, val); } } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index c1a5aa15af8f..5ef7080e14d0 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -256,7 +256,7 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ - if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ + if (value > 0x3f) /* 0 to 0x3f are values */ return 1; spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); -- cgit v1.2.2 From 6a5f96ce72b9e1a4bf06422df53fa819947d2293 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Oct 2009 12:31:39 +0100 Subject: ALSA: hda - Add a proper ifdef to a debug code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a proper ifdef CONFIG_SND_DEBUG_VERBOSE to avoid a compile warning: sound/pci/hda/patch_intelhdmi.c:406: warning: ‘hdmi_get_channel_count’ defined but not used Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 650de1b4ea8d..4f25f08d332b 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -403,11 +403,13 @@ static void hdmi_stop_infoframe_trans(struct hda_codec *codec, AC_DIPXMIT_DISABLE); } +#ifdef CONFIG_SND_DEBUG_VERBOSE static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } +#endif static void hdmi_set_channel_count(struct hda_codec *codec, hda_nid_t nid, int chs) -- cgit v1.2.2 From 23c4a8812a17f0af2b573a63fc991baa7d3570ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 30 Oct 2009 13:21:49 +0100 Subject: ALSA: hda - Switch to polling mode before disabling MSI When any codec communication error happens, try to switch to the polling mode first before turning off MSI. MSI gets more stable nowadays, thus we should keep it on as much as possible. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d0effa3563e2..a0eface6e99a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -677,6 +677,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } + if (!chip->polling_mode) { + snd_printk(KERN_WARNING SFX "azx_get_response timeout, " + "switching to polling mode: last cmd=0x%08x\n", + chip->last_cmd[addr]); + chip->polling_mode = 1; + goto again; + } + if (chip->msi) { snd_printk(KERN_WARNING SFX "No response from codec, " "disabling MSI: last cmd=0x%08x\n", @@ -692,14 +700,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, goto again; } - if (!chip->polling_mode) { - snd_printk(KERN_WARNING SFX "azx_get_response timeout, " - "switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); - chip->polling_mode = 1; - goto again; - } - if (chip->probing) { /* If this critical timeout happens during the codec probing * phase, this is likely an access to a non-existing codec -- cgit v1.2.2 From d355c82a0191d5a3e971bd5af96cc81fe3ed25b9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 3 Nov 2009 15:47:25 +0100 Subject: ALSA: rename "PC Speaker" and "PC Beep" controls to "Beep" To avoid confusion in control names for the standard analog PC Beep generator using a small Internal PC Speaker, rename all related "PC Speaker" and "PC Beep" controls to "Beep" only. This name is more universal and can be also used on more platforms without confusion. Introduce also "Internal Speaker" in ControlNames.txt for systems with full-featured build-in internal speaker. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_codec.c | 6 +++--- sound/pci/ac97/ac97_patch.c | 12 ++++++------ sound/pci/azt3328.c | 4 ++-- sound/pci/ca0106/ca0106_mixer.c | 4 ++-- sound/pci/cmipci.c | 4 ++-- sound/pci/emu10k1/emumixer.c | 4 ++-- sound/pci/es1938.c | 2 +- sound/pci/hda/patch_cmedia.c | 4 ++-- sound/pci/hda/patch_realtek.c | 4 ++-- sound/pci/hda/patch_sigmatel.c | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 78288dbfc17a..20cb60afb200 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -603,8 +603,8 @@ AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1) }; static const struct snd_kcontrol_new snd_ac97_controls_pc_beep[2] = { -AC97_SINGLE("PC Speaker Playback Switch", AC97_PC_BEEP, 15, 1, 1), -AC97_SINGLE("PC Speaker Playback Volume", AC97_PC_BEEP, 1, 15, 1) +AC97_SINGLE("Beep Playback Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("Beep Playback Volume", AC97_PC_BEEP, 1, 15, 1) }; static const struct snd_kcontrol_new snd_ac97_controls_mic_boost = @@ -1393,7 +1393,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } } - /* build PC Speaker controls */ + /* build Beep controls */ if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) && ((ac97->flags & AC97_HAS_PC_BEEP) || snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) { diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 7337abdbe4e3..139cf3b2b9d7 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -800,12 +800,12 @@ AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1), AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0), AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1), -AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1), -AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1), -AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1), -AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1), -AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1), -AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1), +AC97_SINGLE("Beep to Headphone Switch", AC97_AUX, 15, 1, 1), +AC97_SINGLE("Beep to Headphone Volume", AC97_AUX, 12, 7, 1), +AC97_SINGLE("Beep to Master Switch", AC97_AUX, 11, 1, 1), +AC97_SINGLE("Beep to Master Volume", AC97_AUX, 8, 7, 1), +AC97_SINGLE("Beep to Mono Switch", AC97_AUX, 7, 1, 1), +AC97_SINGLE("Beep to Mono Volume", AC97_AUX, 4, 7, 1), AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1), AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1), diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 8451a0169f32..69867ace7860 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -830,8 +830,8 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { AZF3328_MIXER_SWITCH("Mic Boost (+20dB)", IDX_MIXER_MIC, 6, 0), AZF3328_MIXER_SWITCH("Line Playback Switch", IDX_MIXER_LINEIN, 15, 1), AZF3328_MIXER_VOL_STEREO("Line Playback Volume", IDX_MIXER_LINEIN, 0x1f, 1), - AZF3328_MIXER_SWITCH("PC Speaker Playback Switch", IDX_MIXER_PCBEEP, 15, 1), - AZF3328_MIXER_VOL_SPECIAL("PC Speaker Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1), + AZF3328_MIXER_SWITCH("Beep Playback Switch", IDX_MIXER_PCBEEP, 15, 1), + AZF3328_MIXER_VOL_SPECIAL("Beep Playback Volume", IDX_MIXER_PCBEEP, 0x0f, 1, 1), AZF3328_MIXER_SWITCH("Video Playback Switch", IDX_MIXER_VIDEO, 15, 1), AZF3328_MIXER_VOL_STEREO("Video Playback Volume", IDX_MIXER_VIDEO, 0x1f, 1), AZF3328_MIXER_SWITCH("Aux Playback Switch", IDX_MIXER_AUX, 15, 1), diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index c8c6f437f5b3..8f443a9d61ec 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -792,8 +792,8 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu) "Phone Playback Volume", "Video Playback Switch", "Video Playback Volume", - "PC Speaker Playback Switch", - "PC Speaker Playback Volume", + "Beep Playback Switch", + "Beep Playback Volume", "Mono Output Select", "Capture Source", "Capture Switch", diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index ddcd4a9fd7e6..a312bae08f52 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2302,7 +2302,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = { CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), - CMIPCI_SB_VOL_MONO("PC Speaker Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), + CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), @@ -2310,7 +2310,7 @@ static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = { CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), - CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), + CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), }; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index b0fb6c917c38..05afe06e353a 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1818,8 +1818,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, "Master Playback Switch", "Master Capture Switch", "Master Playback Volume", "Master Capture Volume", "Wave Master Playback Volume", "Master Playback Volume", - "PC Speaker Playback Switch", "PC Speaker Capture Switch", - "PC Speaker Playback Volume", "PC Speaker Capture Volume", + "Beep Playback Switch", "Beep Capture Switch", + "Beep Playback Volume", "Beep Capture Volume", "Phone Playback Switch", "Phone Capture Switch", "Phone Playback Volume", "Phone Capture Volume", "Mic Playback Switch", "Mic Capture Switch", diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 820318ee62c1..fb83e1ffa5cb 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1387,7 +1387,7 @@ ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0, db_scale_line), ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0, db_scale_capture), -ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0), +ES1938_SINGLE("Beep Volume", 0, 0x3c, 0, 7, 0), ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0), ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), { diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 780e1a72114a..85c81feb10cf 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -197,8 +197,8 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = { HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT), - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x23, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x23, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c08ca660daba..08a5b8a55408 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7334,8 +7334,8 @@ static struct snd_kcontrol_new alc882_macpro_mixer[] = { HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), /* FIXME: this looks suspicious... - HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT), */ { } /* end */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 66c0876bf734..426edfa476a2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3221,7 +3221,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, /* check for mute support for the the amp */ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, - "PC Beep Playback Switch", + "Beep Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -3230,7 +3230,7 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, /* check to see if there is volume support for the amp */ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, - "PC Beep Playback Volume", + "Beep Playback Volume", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -3271,7 +3271,7 @@ static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { static int stac92xx_beep_switch_ctl(struct hda_codec *codec) { return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl, - 0, "PC Beep Playback Switch", 0); + 0, "Beep Playback Switch", 0); } #endif -- cgit v1.2.2 From 167eae5a17b3cd44a324dbb972c338e489413f54 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 6 Nov 2009 15:47:50 +0100 Subject: ALSA: hda - Reset pins of IDT/STAC codecs at free Some laptops cause annoying clicks or noises at shutdown/reboot since the speaker pin is set still high. Apply the same procedure used for the suspend to avoid such clicks/noises for freeing the codec, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8eb6508cd991..3087705a8e51 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4327,6 +4327,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void stac92xx_shutup(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + hda_nid_t nid; + + /* reset each pin before powering down DAC/ADC to avoid click noise */ + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wcaps); + if (wid_type == AC_WID_PIN) + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); +} + static void stac92xx_free(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4334,6 +4356,7 @@ static void stac92xx_free(struct hda_codec *codec) if (! spec) return; + stac92xx_shutup(codec); stac92xx_free_jacks(codec); snd_array_free(&spec->events); @@ -4793,24 +4816,7 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) { - struct sigmatel_spec *spec = codec->spec; - int i; - hda_nid_t nid; - - /* reset each pin before powering down DAC/ADC to avoid click noise */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type == AC_WID_PIN) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - - if (spec->eapd_mask) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data & - ~spec->eapd_mask); + stac92xx_shutup(codec); return 0; } #endif -- cgit v1.2.2 From 06fe9fb4182177fb046e6d934f80254dd90956ea Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 28 Sep 2009 21:43:57 -0400 Subject: tree-wide: fix a very frequent spelling mistake something-bility is spelled as something-blity so a grep for 'blit' would find these lines this is so trivial that I didn't split it by subsystem / copy additional maintainers - all changes are to comments The only purpose is to get fewer false positives when grepping around the kernel sources. Signed-off-by: Dirk Hohndel Signed-off-by: Jiri Kosina --- sound/pci/ice1712/juli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index fd948bfd9aef..9c0f78ea2c41 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -380,7 +380,7 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = { * 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 + * an alternative possibility - 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. * -- cgit v1.2.2 From b71a8eb0fa64ec6d00175f479e3ef851703568af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 6 Oct 2009 12:42:51 +0200 Subject: tree-wide: fix typos "selct" + "slect" -> "select" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch was generated by git grep -E -i -l 's(le|el)ct' | xargs -r perl -p -i -e 's/([Ss])(le|el)ct/$1elect/ with only skipping net/netfilter/xt_SECMARK.c and include/linux/netfilter/xt_SECMARK.h which have a struct member called selctx. Signed-off-by: Uwe Kleine-König Signed-off-by: Jiri Kosina --- sound/pci/hda/patch_cirrus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 8ba306856d38..7b0446fa6009 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -947,7 +947,7 @@ static void init_input(struct hda_codec *codec) coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */ if (is_active_pin(codec, CS_DMIC1_PIN_NID)) coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 - * No effect if SPDIF_OUT2 is slected in + * No effect if SPDIF_OUT2 is selected in * IDX_SPDIF_CTL. */ cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); -- cgit v1.2.2 From fb8d1a344dbe963f16249d07eee8415e93f9f3c2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Nov 2009 16:02:29 +0100 Subject: ALSA: hda - Add reboot notifier to each codec Add reboot notifier to each codec so that it can do some workarounds needed for reboot. So far, patch_sigmatel.c calls its shutup routine for avoiding noises at reboot on some HP machines. References: Novell bnc#544779 http://bugzilla.novell.com/show_bug.cgi?id=544779 Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 17 +++++++++++++++++ sound/pci/hda/hda_codec.h | 2 ++ sound/pci/hda/hda_intel.c | 1 + sound/pci/hda/patch_sigmatel.c | 1 + 4 files changed, 21 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2c1366343335..146f95be8737 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3404,6 +3404,23 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } } +/* call each reboot notifier */ +void snd_hda_bus_reboot_notify(struct hda_bus *bus) +{ + struct hda_codec *codec; + + if (!bus) + return; + list_for_each_entry(codec, &bus->codec_list, list) { +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!codec->power_on) + continue; +#endif + if (codec->patch_ops.reboot_notify) + codec->patch_ops.reboot_notify(codec); + } +} + /* * open the digital out in the exclusive mode */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 99552fb5f756..624060837653 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -674,6 +674,7 @@ struct hda_codec_ops { #ifdef CONFIG_SND_HDA_POWER_SAVE int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif + void (*reboot_notify)(struct hda_codec *codec); }; /* record for amp information cache */ @@ -910,6 +911,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, * Misc */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); +void snd_hda_bus_reboot_notify(struct hda_bus *bus); /* * power management diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 55c7da30bb61..0d3e0c9ea812 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2150,6 +2150,7 @@ static int azx_resume(struct pci_dev *pci) static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) { struct azx *chip = container_of(nb, struct azx, reboot_notifier); + snd_hda_bus_reboot_notify(chip->bus); azx_stop_chip(chip); return NOTIFY_OK; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3087705a8e51..9c33700b21a8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4831,6 +4831,7 @@ static struct hda_codec_ops stac92xx_patch_ops = { .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif + .reboot_notify = stac92xx_shutup, }; static int patch_stac9200(struct hda_codec *codec) -- cgit v1.2.2 From e3303235209c0496b490e10ab131e72a9568c153 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 10 Nov 2009 14:53:02 +0100 Subject: ALSA: hda - proc - show which I/O NID is associated to PCM device Output something like: Node 0x02 [Audio Output] wcaps 0x11: Stereo Device: name="ALC888 Analog", type="Audio", device=0, substream=0 Converter: stream=0, channel=0 ... Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 +++++---- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_proc.c | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 146f95be8737..480d1ec49c99 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2877,14 +2877,15 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { + "Audio", "SPDIF", "HDMI", "Modem" +}; + /* * get the empty PCM device number to assign */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { - static const char *dev_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" - }; /* audio device indices; not linear to keep compatibility */ static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, @@ -2903,7 +2904,7 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) return audio_idx[type][i]; - snd_printk(KERN_WARNING "Too many %s devices\n", dev_name[type]); + snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]); return -EAGAIN; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 624060837653..cbf199a98ab2 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -894,6 +894,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ +extern const char *snd_hda_pcm_type_name[]; int snd_hda_build_pcms(struct hda_bus *bus); int snd_hda_codec_build_pcms(struct hda_codec *codec); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 95f24e4729f8..f5639c2988ab 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -309,7 +309,21 @@ static void print_audio_io(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, unsigned int wid_type) { - int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + int pcm, conv; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + int type; + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + for (type = 0; type < 2; type++) { + if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) + continue; + snd_iprintf(buffer, " Device: name=\"%s\", type=\"%s\", device=%i, substream=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device, + cpcm->pcm->streams[type].substream->number); + } + } + conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); snd_iprintf(buffer, " Converter: stream=%d, channel=%d\n", (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, -- cgit v1.2.2 From 8f217a226cfa7b960b8a6c00cef6b4de2c5dd030 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 Nov 2009 18:26:12 +0100 Subject: ALSA: hda - Add missing export for snd_hda_bus_reboot_notify ... forgot to add for modules. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 480d1ec49c99..2b787b013e93 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3421,6 +3421,7 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) codec->patch_ops.reboot_notify(codec); } } +EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); /* * open the digital out in the exclusive mode -- cgit v1.2.2 From a2f6309e8392e2c14c04594fca8b4876c8c9bc36 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Nov 2009 09:34:25 +0100 Subject: ALSA: hda - Add power on/off counter Added the power on/off counter and expose via sysfs files. The sysfs files, power_on_acct and power_off_acct, are created under each codec hwdep sysfs directory (e.g. /sys/class/sound/hwC0D0). The files show the msec length of the codec power-on and power-off, respectively. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 16 ++++++++++++++++ sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_hwdep.c | 38 ++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_local.h | 9 +++++++++ 4 files changed, 67 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2b787b013e93..444d9039c1ac 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -515,6 +515,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { snd_hda_hwdep_add_sysfs(codec); + snd_hda_hwdep_add_power_sysfs(codec); } return 0; } @@ -2452,9 +2453,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); #ifdef CONFIG_SND_HDA_POWER_SAVE + snd_hda_update_power_acct(codec); cancel_delayed_work(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; + codec->power_jiffies = jiffies; #endif } @@ -3191,6 +3194,17 @@ static void hda_keep_power_on(struct hda_codec *codec) { codec->power_count++; codec->power_on = 1; + codec->power_jiffies = jiffies; +} + +void snd_hda_update_power_acct(struct hda_codec *codec) +{ + unsigned long delta = jiffies - codec->power_jiffies; + if (codec->power_on) + codec->power_on_acct += delta; + else + codec->power_off_acct += delta; + codec->power_jiffies += delta; } void snd_hda_power_up(struct hda_codec *codec) @@ -3201,7 +3215,9 @@ void snd_hda_power_up(struct hda_codec *codec) if (codec->power_on || codec->power_transition) return; + snd_hda_update_power_acct(codec); codec->power_on = 1; + codec->power_jiffies = jiffies; if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cbf199a98ab2..b16678cade18 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -812,6 +812,9 @@ struct hda_codec { unsigned int power_transition :1; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ + unsigned long power_on_acct; + unsigned long power_off_acct; + unsigned long power_jiffies; #endif /* codec-specific additional proc output */ @@ -936,6 +939,7 @@ const char *snd_hda_get_jack_location(u32 cfg); void snd_hda_power_up(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec); #define snd_hda_codec_needs_resume(codec) codec->power_count +void snd_hda_update_power_acct(struct hda_codec *codec); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index cc24e6721d74..d24328661c6a 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static ssize_t power_on_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct)); +} + +static ssize_t power_off_acct_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + snd_hda_update_power_acct(codec); + return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct)); +} + +static struct device_attribute power_attrs[] = { + __ATTR_RO(power_on_acct), + __ATTR_RO(power_off_acct), +}; + +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + struct snd_hwdep *hwdep = codec->hwdep; + int i; + + for (i = 0; i < ARRAY_SIZE(power_attrs); i++) + snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, + hwdep->device, &power_attrs[i]); + return 0; +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + #ifdef CONFIG_SND_HDA_RECONFIG /* diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 461e0c15c77a..015fbac914b3 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -437,6 +437,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif +#ifdef CONFIG_SND_HDA_POWER_SAVE +int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); +#else +static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) +{ + return 0; +} +#endif + #ifdef CONFIG_SND_HDA_RECONFIG int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); #else -- cgit v1.2.2 From f8b7163529831ee3ad6a1aeaa0f1256cb527008d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Nov 2009 09:50:28 +0100 Subject: ALSA: hda - Don't access invalid substream in proc file The commit e3303235209c0496b490e10ab131e72a9568c153 "ALSA: hda - proc - show which I/O NID is associated to PCM device" introduces the access to substream pointer. But, PCMs may have no substreams in one or both directions, and this results in NULL dereference. Also, print the first substream number doesn't make sense. This patch removes the access to the substream pointer, and reformat to fit to the standard coding style. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_proc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f5639c2988ab..f5b783ce450d 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -316,11 +316,11 @@ static void print_audio_io(struct snd_info_buffer *buffer, for (type = 0; type < 2; type++) { if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) continue; - snd_iprintf(buffer, " Device: name=\"%s\", type=\"%s\", device=%i, substream=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device, - cpcm->pcm->streams[type].substream->number); + snd_iprintf(buffer, " Device: name=\"%s\", " + "type=\"%s\", device=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device); } } conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); -- cgit v1.2.2 From 7288561af9a931c59e431336b553d897ee37b67d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Nov 2009 10:01:18 +0100 Subject: ALSA: hda - Fix build error without CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_POWER_SAVE is independent from CONFIG_SND_HDA_HWDEP. Thus snd_hda_hwdep_add_power_sysfs() needs the check of both kconfigs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_local.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 015fbac914b3..c1ca3182e6a4 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -437,7 +437,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec); static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } #endif -#ifdef CONFIG_SND_HDA_POWER_SAVE +#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP) int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec); #else static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec) -- cgit v1.2.2 From 401de8184a4d94688962b9258fe10ab309ffda9c Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 13 Nov 2009 16:02:56 +0900 Subject: ALSA: ice1712: Use bitrev8 Signed-off-by: Akinobu Mita Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 75c602b5b132..351654cf7b09 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -570,6 +570,7 @@ config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" select SND_MPU401_UART select SND_AC97_CODEC + select BITREVERSE help Say Y here to include support for soundcards based on the ICE1712 (Envy24) chip. -- cgit v1.2.2 From 01a1796bc52f625edc23bf995d200e1556eec544 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Fri, 13 Nov 2009 16:47:10 -0800 Subject: sound/pci/hda/patch_via.c: work around gcc-4.0.2 ICE sound/pci/hda/patch_via.c: In function 'via_hp_bind_automute': sound/pci/hda/patch_via.c:2074: internal compiler error: in do_SUBST, at combine.c:462 Please submit a full bug report, with preprocessed source if appropriate. See for instructions. [added a comment by tiwai] Signed-off-by: Andrew Morton Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5ec0e39593b5..5a856009c916 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2043,7 +2043,10 @@ static void via_speaker_automute(struct hda_codec *codec) /* mute line-out and internal speaker if HP is plugged */ static void via_hp_bind_automute(struct hda_codec *codec) { - unsigned int hp_present, present = 0; + /* use long instead of int below just to avoid an internal compiler + * error with gcc 4.0.x + */ + unsigned long hp_present, present = 0; struct via_spec *spec = codec->spec; int i; -- cgit v1.2.2 From 50d40f187f9182ee8caa1b83f80a0e11e2226baa Mon Sep 17 00:00:00 2001 From: Aleksey Kunitskiy Date: Sat, 14 Nov 2009 15:18:54 +0200 Subject: ALSA: ice1724 - Patch for suspend/resume for ESI Juli@ Add proper suspend/resume code for Juli@ cards. Based on ice1724 suspend/resume work of Igor Chernyshev. Fixes bug https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4413 Tested on linux-2.6.31.6 Signed-off-by: Aleksey Kunitskiy Signed-off-by: Takashi Iwai --- sound/pci/ice1712/juli.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index fd948bfd9aef..f5020ad99a10 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -503,6 +503,31 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice) return 0; } +/* + * suspend/resume + * */ + +#ifdef CONFIG_PM +static int juli_resume(struct snd_ice1712 *ice) +{ + struct snd_akm4xxx *ak = ice->akm; + struct juli_spec *spec = ice->spec; + /* akm4358 un-reset, un-mute */ + snd_akm4xxx_reset(ak, 0); + /* reinit ak4114 */ + snd_ak4114_reinit(spec->ak4114); + return 0; +} + +static int juli_suspend(struct snd_ice1712 *ice) +{ + struct snd_akm4xxx *ak = ice->akm; + /* akm4358 reset and soft-mute */ + snd_akm4xxx_reset(ak, 1); + return 0; +} +#endif + /* * initialize the chip */ @@ -646,6 +671,13 @@ static int __devinit juli_init(struct snd_ice1712 *ice) ice->set_spdif_clock = juli_set_spdif_clock; ice->spdif.ops.open = juli_spdif_in_open; + +#ifdef CONFIG_PM + ice->pm_resume = juli_resume; + ice->pm_suspend = juli_suspend; + ice->pm_suspend_enabled = 1; +#endif + return 0; } -- cgit v1.2.2 From 123c07aeddd71fbb295842a8c19866e780b9a100 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 21 Oct 2009 14:48:23 +0200 Subject: ALSA: hda_intel: Digital PC Beep - change behaviour for input layer Original implementation was keeping registered input device for SND_BEEP and SND_TONE events all time. This patch changes this behaviour: If digital PC Beep is turned off using universal control switch, the input device is unregistered. Explanation: The kd_mksound() send SND_BEEP and SND_TONE only to last registered device acceping those events. It means that the HDA Intel audio driver blocks also the internal PC Speaker device (pcspkr.c driver) even if the HDA Beep is muted. The user can easy disable all beeps using 'setterm -blength 0' or 'xset b off' command. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 88 +++++++++++++++++++++++++++++++++--------- sound/pci/hda/hda_beep.h | 4 ++ sound/pci/hda/hda_codec.c | 12 ++++++ sound/pci/hda/hda_local.h | 15 +++++++ sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_sigmatel.c | 16 ++++---- 7 files changed, 111 insertions(+), 28 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 3f51a981e604..0e986537d570 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, return 0; } -int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +static void snd_hda_do_detach(struct hda_beep *beep) +{ + input_unregister_device(beep->dev); + beep->dev = NULL; + cancel_work_sync(&beep->beep_work); + /* turn off beep for sure */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); +} + +static int snd_hda_do_attach(struct hda_beep *beep) { struct input_dev *input_dev; - struct hda_beep *beep; + struct hda_codec *codec = beep->codec; int err; - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly */ - - beep = kzalloc(sizeof(*beep), GFP_KERNEL); - if (beep == NULL) - return -ENOMEM; - snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); input_dev = input_allocate_device(); if (!input_dev) { - kfree(beep); + printk(KERN_INFO "hda_beep: unable to allocate input device\n"); return -ENOMEM; } @@ -151,21 +153,71 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) err = input_register_device(input_dev); if (err < 0) { input_free_device(input_dev); - kfree(beep); + printk(KERN_INFO "hda_beep: unable to register input device\n"); return err; } + beep->dev = input_dev; + return 0; +} + +static void snd_hda_do_register(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, register_work); + int request; + + mutex_lock(&beep->mutex); + request = beep->request_enable; + if (beep->enabled != request) { + if (!request) { + snd_hda_do_detach(beep); + } else { + if (snd_hda_do_attach(beep) < 0) + goto __out; + } + beep->enabled = request; + } + __out: + mutex_unlock(&beep->mutex); +} + +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) +{ + struct hda_beep *beep = codec->beep; + enable = !!enable; + if (beep && beep->enabled != enable) { + beep->request_enable = enable; + schedule_work(&beep->register_work); + return 1; + } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device); + +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +{ + struct hda_beep *beep; + + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly */ + beep = kzalloc(sizeof(*beep), GFP_KERNEL); + if (beep == NULL) + return -ENOMEM; + snprintf(beep->phys, sizeof(beep->phys), + "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); beep->nid = nid; - beep->dev = input_dev; beep->codec = codec; - beep->enabled = 1; codec->beep = beep; + INIT_WORK(&beep->register_work, &snd_hda_do_register); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); + mutex_init(&beep->mutex); + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); @@ -174,11 +226,11 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; if (beep) { - cancel_work_sync(&beep->beep_work); - - input_unregister_device(beep->dev); - kfree(beep); + cancel_work_sync(&beep->register_work); + if (beep->enabled) + snd_hda_do_detach(beep); codec->beep = NULL; + kfree(beep); } } EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 0c3de787c717..68465f679d8c 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -32,11 +32,15 @@ struct hda_beep { int tone; hda_nid_t nid; unsigned int enabled:1; + unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ + struct work_struct register_work; /* scheduled task for beep event */ struct work_struct beep_work; /* scheduled task for beep event */ + struct mutex mutex; }; #ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); void snd_hda_detach_beep_device(struct hda_codec *codec); #else diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 444d9039c1ac..7fd2abe1129d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -30,6 +30,7 @@ #include #include #include "hda_local.h" +#include "hda_beep.h" #include /* @@ -1734,6 +1735,17 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + + snd_hda_enable_beep_device(codec, *valp); + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); + /* * bound volume controls * diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c1ca3182e6a4..3001794ad291 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -66,6 +66,19 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = snd_hda_mixer_amp_switch_put_beep, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +/* special beep mono mute switch */ +#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) +/* special beep stereo mute switch */ +#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ + HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); @@ -81,6 +94,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d603f6aba63..a0293614a0b9 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -159,7 +159,7 @@ static void ad198x_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 49de107db16b..8c04e0e0f655 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2413,7 +2413,7 @@ static void alc_free_kctls(struct hda_codec *codec); /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8d65d2b25234..87ba239ff1c9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2648,6 +2648,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MUTE_BEEP, STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_HP_SWITCH, STAC_CTL_WIDGET_IO_SWITCH, @@ -2658,6 +2659,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0), STAC_MONO_MUX, STAC_CODEC_HP_SWITCH(NULL), STAC_CODEC_IO_SWITCH(NULL, 0), @@ -3221,11 +3223,14 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); - int err; + int err, type = STAC_CTL_WIDGET_MUTE_BEEP; + + if (spec->anabeep_nid == nid) + type = STAC_CTL_WIDGET_MUTE; /* check for mute support for the the amp */ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { - err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + err = stac92xx_add_control(spec, type, "Beep Playback Switch", HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); if (err < 0) @@ -3258,12 +3263,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int enabled = !!ucontrol->value.integer.value[0]; - if (codec->beep->enabled != enabled) { - codec->beep->enabled = enabled; - return 1; - } - return 0; + return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]); } static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { -- cgit v1.2.2 From 13dab0808bb41b18888e1758a060a685deee1f30 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 3 Nov 2009 14:29:50 +0100 Subject: ALSA: hda_intel: Digital PC Beep - delay input device unregistration The massive register/unregister calls for input device layer might be overkill. Delay unregister call by one HZ as workaround. Also, as benefit, beep->enabled variable is changed immediately now (not from workqueue). Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 42 +++++++++++++++++++++++++++--------------- sound/pci/hda/hda_beep.h | 3 ++- 2 files changed, 29 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 0e986537d570..74db40edb336 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -164,20 +164,21 @@ static void snd_hda_do_register(struct work_struct *work) { struct hda_beep *beep = container_of(work, struct hda_beep, register_work); - int request; mutex_lock(&beep->mutex); - request = beep->request_enable; - if (beep->enabled != request) { - if (!request) { - snd_hda_do_detach(beep); - } else { - if (snd_hda_do_attach(beep) < 0) - goto __out; - } - beep->enabled = request; - } - __out: + if (beep->enabled && !beep->dev) + snd_hda_do_attach(beep); + mutex_unlock(&beep->mutex); +} + +static void snd_hda_do_unregister(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, unregister_work.work); + + mutex_lock(&beep->mutex); + if (!beep->enabled && beep->dev) + snd_hda_do_detach(beep); mutex_unlock(&beep->mutex); } @@ -185,9 +186,19 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) { struct hda_beep *beep = codec->beep; enable = !!enable; - if (beep && beep->enabled != enable) { - beep->request_enable = enable; - schedule_work(&beep->register_work); + if (beep == NULL) + return 0; + if (beep->enabled != enable) { + beep->enabled = enable; + if (enable) { + cancel_delayed_work(&beep->unregister_work); + schedule_work(&beep->register_work); + } else { + /* turn off beep */ + snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, 0); + schedule_delayed_work(&beep->unregister_work, HZ); + } return 1; } return 0; @@ -215,6 +226,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) codec->beep = beep; INIT_WORK(&beep->register_work, &snd_hda_do_register); + INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister); INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); mutex_init(&beep->mutex); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 68465f679d8c..53eba8d8414d 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -34,7 +34,8 @@ struct hda_beep { unsigned int enabled:1; unsigned int request_enable:1; unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */ - struct work_struct register_work; /* scheduled task for beep event */ + struct work_struct register_work; /* registration work */ + struct delayed_work unregister_work; /* unregistration work */ struct work_struct beep_work; /* scheduled task for beep event */ struct mutex mutex; }; -- cgit v1.2.2 From 5f81669750504b1e7e00acde5068d972af466f29 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 4 Nov 2009 12:46:49 +0100 Subject: ALSA: hda: beep - add missing cancel_delayed_work The unregister work should be also canceled in snd_hda_detach_beep_device() function. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 74db40edb336..c819152de79b 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -239,6 +239,7 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) struct hda_beep *beep = codec->beep; if (beep) { cancel_work_sync(&beep->register_work); + cancel_delayed_work(&beep->unregister_work); if (beep->enabled) snd_hda_do_detach(beep); codec->beep = NULL; -- cgit v1.2.2 From 2dca0bba70ce3c233be152e384580c134935332d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 13 Nov 2009 18:41:52 +0100 Subject: ALSA: hda - add beep_mode module parameter The beep_mode parameter for snd-hda-intel module allows to choose among different digital beep device registation to the input layer. 0 = do not register to the input layer 1 = register to the input layer all time 2 = use "Beep Switch" control exported to user space mixer applications Also, introduce CONFIG_SND_HDA_INPUT_BEEP_MODE for default value. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 11 +++++++++++ sound/pci/hda/hda_beep.c | 21 ++++++++++++++++----- sound/pci/hda/hda_beep.h | 5 +++++ sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_intel.c | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 55545e0818b5..25ae10e16f59 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -38,6 +38,17 @@ config SND_HDA_INPUT_BEEP Say Y here to build a digital beep interface for HD-audio driver. This interface is used to generate digital beeps. +config SND_HDA_INPUT_BEEP_MODE + int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)" + depends on SND_HDA_INPUT_BEEP=y + default "1" + range 0 2 + help + Set 0 to disable the digital beep interface for HD-audio by default. + Set 1 to always enable the digital beep interface for HD-audio by + default. Set 2 to control the beep device registration to input + layer using a "Beep Switch" in mixer applications. + config SND_HDA_INPUT_JACK bool "Support jack plugging notification via input layer" depends on INPUT=y || INPUT=SND_HDA_INTEL diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index c819152de79b..9e48798b415b 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -190,14 +190,19 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) return 0; if (beep->enabled != enable) { beep->enabled = enable; - if (enable) { - cancel_delayed_work(&beep->unregister_work); - schedule_work(&beep->register_work); - } else { + if (!enable) { /* turn off beep */ snd_hda_codec_write_cache(beep->codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, 0); - schedule_delayed_work(&beep->unregister_work, HZ); + } + if (beep->mode == HDA_BEEP_MODE_SWREG) { + if (enable) { + cancel_delayed_work(&beep->unregister_work); + schedule_work(&beep->register_work); + } else { + schedule_delayed_work(&beep->unregister_work, + HZ); + } } return 1; } @@ -223,6 +228,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) beep->nid = nid; beep->codec = codec; + beep->mode = codec->beep_mode; codec->beep = beep; INIT_WORK(&beep->register_work, &snd_hda_do_register); @@ -230,6 +236,11 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); mutex_init(&beep->mutex); + if (beep->mode == HDA_BEEP_MODE_ON) { + beep->enabled = 1; + snd_hda_do_register(&beep->register_work); + } + return 0; } EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 53eba8d8414d..17dd1c325e32 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -24,10 +24,15 @@ #include "hda_codec.h" +#define HDA_BEEP_MODE_ON 0 +#define HDA_BEEP_MODE_OFF 1 +#define HDA_BEEP_MODE_SWREG 2 + /* beep information */ struct hda_beep { struct input_dev *dev; struct hda_codec *codec; + unsigned int mode; char phys[32]; int tone; hda_nid_t nid; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b16678cade18..51920563bc7f 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -772,6 +772,7 @@ struct hda_codec { /* beep device */ struct hda_beep *beep; + unsigned int beep_mode; /* widget capabilities cache */ unsigned int num_nodes; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e73e395e7601..91bcbdad5af5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -64,6 +64,10 @@ static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = + CONFIG_SND_HDA_INPUT_BEEP_MODE}; +#endif module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); module_param_array(patch, charp, NULL, 0444); MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface."); #endif +#ifdef CONFIG_SND_HDA_INPUT_BEEP +module_param_array(beep_mode, int, NULL, 0444); +MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " + "(0=off, 1=on, 2=mute switch on/off) (default=1)."); +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; @@ -404,6 +413,7 @@ struct azx { unsigned short codec_mask; int codec_probe_mask; /* copied from probe_mask option */ struct hda_bus *bus; + unsigned int beep_mode; /* CORB/RIRB */ struct azx_rb corb; @@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) continue; + codec->beep_mode = chip->beep_mode; codecs++; } } @@ -2579,6 +2590,10 @@ static int __devinit azx_probe(struct pci_dev *pci, goto out_free; card->private_data = chip; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + chip->beep_mode = beep_mode[dev]; +#endif + /* create codec instances */ err = azx_codec_create(chip, model[dev]); if (err < 0) -- cgit v1.2.2 From 3911a4c19e927738766003839aa447becbdbaa27 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 11 Nov 2009 13:43:01 +0100 Subject: ALSA: hda - proc - introduce Control: lines to show mixer<->NID assignment This is an initial patch to show universal control<->NID assigment in proc codec file. The change helps to debug codec related problems. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 32 ++++++++++++------------ sound/pci/hda/hda_generic.c | 17 ++++++++----- sound/pci/hda/hda_local.h | 11 +++++++-- sound/pci/hda/hda_proc.c | 55 ++++++++++++++++++++++++++++++------------ sound/pci/hda/patch_analog.c | 4 ++- sound/pci/hda/patch_ca0110.c | 4 +-- sound/pci/hda/patch_cirrus.c | 12 ++++----- sound/pci/hda/patch_realtek.c | 3 ++- sound/pci/hda/patch_sigmatel.c | 4 +-- 9 files changed, 92 insertions(+), 50 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7fd2abe1129d..1ed1d88e1834 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -946,7 +946,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1517,18 +1517,20 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); /* Add a control element and assign to the codec */ -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl) +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl) { int err; - struct snd_kcontrol **knewp; + struct hda_nid_item *item; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; - knewp = snd_array_new(&codec->mixers); - if (!knewp) + item = snd_array_new(&codec->mixers); + if (!item) return -ENOMEM; - *knewp = kctl; + item->kctl = kctl; + item->nid = nid; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); @@ -1537,9 +1539,9 @@ EXPORT_SYMBOL_HDA(snd_hda_ctl_add); void snd_hda_ctls_clear(struct hda_codec *codec) { int i; - struct snd_kcontrol **kctls = codec->mixers.list; + struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, kctls[i]); + snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); } @@ -1645,7 +1647,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, kctl = snd_ctl_make_virtual_master(name, tlv); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; @@ -2139,7 +2141,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return -ENOMEM; kctl->id.index = idx; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -2184,8 +2186,8 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, if (!mout->dig_out_nid) return 0; /* ATTENTION: here mout is passed as private_data, instead of codec */ - return snd_hda_ctl_add(codec, - snd_ctl_new1(&spdif_share_sw, mout)); + return snd_hda_ctl_add(codec, mout->dig_out_nid, + snd_ctl_new1(&spdif_share_sw, mout)); } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); @@ -2289,7 +2291,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) if (!kctl) return -ENOMEM; kctl->private_value = nid; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, nid, kctl); if (err < 0) return err; } @@ -3165,7 +3167,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) { if (!codec->addr) return err; @@ -3173,7 +3175,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) if (!kctl) return -ENOMEM; kctl->id.device = codec->addr; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b36f6c5a92df..092c6a7c2ff3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_INPUT, index); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, if (is_loopback) add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; created = 1; @@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec)); + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec)); if (err < 0) return err; @@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec) HDA_CODEC_VOLUME(name, adc_node->nid, spec->input_mux.items[i].index, HDA_INPUT); - err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + err = snd_hda_ctl_add(codec, adc_node->nid, + snd_ctl_new1(&knew, codec)); if (err < 0) return err; } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3001794ad291..e6a0918f70d3 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -440,7 +440,13 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl); +struct hda_nid_item { + struct snd_kcontrol *kctl; + hda_nid_t nid; +}; + +int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, + struct snd_kcontrol *kctl); void snd_hda_ctls_clear(struct hda_codec *codec); /* @@ -514,7 +520,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, * AMP control callbacks */ /* retrieve parameters from private_value */ -#define get_amp_nid(kc) ((kc)->private_value & 0xffff) +#define get_amp_nid_(pv) ((pv) & 0xffff) +#define get_amp_nid(kc) get_amp_nid_((kc)->private_value) #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f5b783ce450d..f465cff28041 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -46,6 +46,41 @@ static const char *get_wid_type_name(unsigned int wid_value) return "UNKNOWN Widget"; } +static void print_nid_mixers(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int i; + struct hda_nid_item *items = codec->mixers.list; + struct snd_kcontrol *kctl; + for (i = 0; i < codec->mixers.used; i++) { + if (items[i].nid == nid) { + kctl = items[i].kctl; + snd_iprintf(buffer, + " Control: name=\"%s\", index=%i, device=%i\n", + kctl->id.name, kctl->id.index, kctl->id.device); + } + } +} + +static void print_nid_pcms(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int pcm, type; + struct hda_pcm *cpcm; + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + cpcm = &codec->pcm_info[pcm]; + for (type = 0; type < 2; type++) { + if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) + continue; + snd_iprintf(buffer, " Device: name=\"%s\", " + "type=\"%s\", device=%i\n", + cpcm->name, + snd_hda_pcm_type_name[cpcm->pcm_type], + cpcm->pcm->device); + } + } +} + static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { @@ -309,21 +344,7 @@ static void print_audio_io(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, unsigned int wid_type) { - int pcm, conv; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - int type; - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - for (type = 0; type < 2; type++) { - if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) - continue; - snd_iprintf(buffer, " Device: name=\"%s\", " - "type=\"%s\", device=%i\n", - cpcm->name, - snd_hda_pcm_type_name[cpcm->pcm_type], - cpcm->pcm->device); - } - } - conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); snd_iprintf(buffer, " Converter: stream=%d, channel=%d\n", (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, @@ -471,6 +492,7 @@ static void print_gpio(struct snd_info_buffer *buffer, (data & (1<private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), + kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index d08353d3bb7f..af478019088e 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, @@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx, struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type); sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]); - return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } #define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 8ba306856d38..9ac09e4568b3 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, *kctlp); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static int add_volume(struct hda_codec *codec, const char *name, @@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); - return snd_hda_ctl_add(codec, *kctlp); + return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) @@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) spec->vmaster_sw = snd_ctl_make_virtual_master("Master Playback Switch", NULL); - err = snd_hda_ctl_add(codec, spec->vmaster_sw); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); if (err < 0) return err; snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); spec->vmaster_vol = snd_ctl_make_virtual_master("Master Playback Volume", tlv); - err = snd_hda_ctl_add(codec, spec->vmaster_vol); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); if (err < 0) return err; return 0; @@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = (long)spec->capture_bind[i]; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } if (spec->num_inputs > 1 && !spec->mic_detect) { - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cs_capture_source, codec)); if (err < 0) return err; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8c04e0e0f655..fff9de245646 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2461,7 +2461,8 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), kctl); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 87ba239ff1c9..a3872b90d6ed 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1085,7 +1085,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (!spec->auto_mic && spec->num_dmuxes > 0 && snd_hda_get_bool_hint(codec, "separate_dmux") == 1) { stac_dmux_mixer.count = spec->num_dmuxes; - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_dmux_mixer, codec)); if (err < 0) return err; @@ -1101,7 +1101,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) spec->spdif_mute = 1; } stac_smux_mixer.count = spec->num_smuxes; - err = snd_hda_ctl_add(codec, + err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&stac_smux_mixer, codec)); if (err < 0) return err; -- cgit v1.2.2 From 4d02d1b638af580ae3d69367248539a8b3893064 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 12 Nov 2009 10:15:48 +0100 Subject: ALSA: hda - proc - add support for dynamic controls to mixer<->NID mapping This patch adds support for dynamically created controls to proc codec file (Control: lines). Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 7 ++++++- sound/pci/hda/hda_local.h | 3 +++ sound/pci/hda/patch_analog.c | 2 ++ sound/pci/hda/patch_realtek.c | 2 ++ sound/pci/hda/patch_sigmatel.c | 10 +++++++--- sound/pci/hda/patch_via.c | 2 ++ 6 files changed, 22 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 1ed1d88e1834..d71e651046eb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1523,6 +1523,11 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_nid_item *item; + if (kctl->id.subdevice & (1<<31)) { + if (nid == 0) + nid = kctl->id.subdevice & 0xffff; + kctl->id.subdevice = 0; + } err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; @@ -3160,7 +3165,7 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config); */ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) { - int err; + int err; for (; knew->name; knew++) { struct snd_kcontrol *kctl; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index e6a0918f70d3..3bfcf42ff6cf 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -33,6 +33,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -53,6 +54,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -69,6 +71,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ + .subdevice = (1<<31)|(nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index ef3383912b6e..2d345606265b 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2571,6 +2571,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fff9de245646..c0a98e724a25 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4323,6 +4323,8 @@ static int add_control(struct alc_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index a3872b90d6ed..d2ddb959c290 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2671,7 +2671,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { static struct snd_kcontrol_new * stac_control_new(struct sigmatel_spec *spec, struct snd_kcontrol_new *ktemp, - const char *name) + const char *name, + hda_nid_t nid) { struct snd_kcontrol_new *knew; @@ -2687,6 +2688,8 @@ stac_control_new(struct sigmatel_spec *spec, spec->kctls.alloced--; return NULL; } + if (nid) + knew->subdevice = (1<<31)|nid; return knew; } @@ -2695,7 +2698,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec, int idx, const char *name, unsigned long val) { - struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name); + struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, + get_amp_nid_(val)); if (!knew) return -ENOMEM; knew->index = idx; @@ -2766,7 +2770,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) if (!spec->num_adcs || imux->num_items <= 1) return 0; /* no need for input source control */ knew = stac_control_new(spec, &stac_input_src_temp, - stac_input_src_temp.name); + stac_input_src_temp.name, 0); if (!knew) return -ENOMEM; knew->count = spec->num_adcs; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5a856009c916..14219d898b2e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -442,6 +442,8 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = (1<<31)|get_amp_nid_(val); knew->private_value = val; return 0; } -- cgit v1.2.2 From 9c96fa599fe4f0ccc6e3e606df6652335afe28e8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 11:25:33 +0100 Subject: ALSA: hda - Get rid of magic digits for subdev hack Define a proper const for a magic 31bit flag for subdev / NID setup with a brief comment. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_local.h | 15 ++++++++++++--- sound/pci/hda/patch_analog.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/hda/patch_sigmatel.c | 2 +- sound/pci/hda/patch_via.c | 2 +- 6 files changed, 17 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d71e651046eb..5e21b35207ab 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1523,7 +1523,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_nid_item *item; - if (kctl->id.subdevice & (1<<31)) { + if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { if (nid == 0) nid = kctl->id.subdevice & 0xffff; kctl->id.subdevice = 0; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3bfcf42ff6cf..4e77f4747291 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -23,6 +23,15 @@ #ifndef __SOUND_HDA_LOCAL_H #define __SOUND_HDA_LOCAL_H +/* We abuse kcontrol_new.subdev field to pass the NID corresponding to + * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, + * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. + * + * Note that the subdevice field is cleared again before the real registration + * in snd_hda_ctl_add(), so that this value won't appear in the outside. + */ +#define HDA_SUBDEV_NID_FLAG (1U << 31) + /* * for mixer controls */ @@ -33,7 +42,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -54,7 +63,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -71,7 +80,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = (1<<31)|(nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2d345606265b..ceb0c603da04 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2572,7 +2572,7 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, if (! knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c0a98e724a25..eee3143eef75 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4324,7 +4324,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d2ddb959c290..7f76a97954f9 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2689,7 +2689,7 @@ stac_control_new(struct sigmatel_spec *spec, return NULL; } if (nid) - knew->subdevice = (1<<31)|nid; + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; return knew; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 14219d898b2e..0c621d74b165 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -443,7 +443,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = (1<<31)|get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } -- cgit v1.2.2 From 85dd662ff4d2967084acfc761a33717383297e42 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 11 Nov 2009 13:49:07 +0100 Subject: ALSA: hda - move snd_hda_pcm_type_name from hda_codec.h to hda_local.h The snd_hda_pcm_type_name array is local only. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 - sound/pci/hda/hda_local.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 51920563bc7f..be6c5f443cd9 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -898,7 +898,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ -extern const char *snd_hda_pcm_type_name[]; int snd_hda_build_pcms(struct hda_bus *bus); int snd_hda_codec_build_pcms(struct hda_codec *codec); void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4e77f4747291..7c049839ea26 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -92,6 +92,8 @@ #define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction) +extern const char *snd_hda_pcm_type_name[]; + int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, -- cgit v1.2.2 From d5191e50b251594bdde10d4839a952ff1646ef62 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 14:58:17 +0100 Subject: ALSA: hda - Update / add kerneldoc comments to exported functions Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 432 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 391 insertions(+), 41 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5e21b35207ab..e344235da491 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -94,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec); static inline void hda_keep_power_on(struct hda_codec *codec) {} #endif +/** + * snd_hda_get_jack_location - Give a location string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack location, e.g. "Rear", "Front", etc. + */ const char *snd_hda_get_jack_location(u32 cfg) { static char *bases[7] = { @@ -121,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); +/** + * snd_hda_get_jack_connectivity - Give a connectivity string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack connectivity, i.e. external or internal connection. + */ const char *snd_hda_get_jack_connectivity(u32 cfg) { static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; @@ -129,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg) } EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); +/** + * snd_hda_get_jack_type - Give a type string of the jack + * @cfg: pin default config value + * + * Parse the pin default config value and returns the string of the + * jack type, i.e. the purpose of the jack, such as Line-Out or CD. + */ const char *snd_hda_get_jack_type(u32 cfg) { static char *jack_types[16] = { @@ -822,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, return 0; } +/** + * snd_hda_codec_set_pincfg - Override a pin default configuration + * @codec: the HDA codec + * @nid: NID to set the pin config + * @cfg: the pin default config value + * + * Override a pin default configuration value in the cache. + * This value can be read by snd_hda_codec_get_pincfg() in a higher + * priority than the real hardware value. + */ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg) { @@ -829,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg); -/* get the current pin config value of the given pin NID */ +/** + * snd_hda_codec_get_pincfg - Obtain a pin-default configuration + * @codec: the HDA codec + * @nid: NID to get the pin config + * + * Get the current pin config value of the given pin NID. + * If the pincfg value is cached or overridden via sysfs or driver, + * returns the cached value. + */ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; @@ -1028,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr } EXPORT_SYMBOL_HDA(snd_hda_codec_new); +/** + * snd_hda_codec_configure - (Re-)configure the HD-audio codec + * @codec: the HDA codec + * + * Start parsing of the given codec tree and (re-)initialize the whole + * patch instance. + * + * Returns 0 if successful or a negative error code. + */ int snd_hda_codec_configure(struct hda_codec *codec) { int err; @@ -1090,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); +/** + * snd_hda_codec_cleanup_stream - clean up the codec for closing + * @codec: the CODEC to clean up + * @nid: the NID to clean up + */ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) { if (!nid) @@ -1165,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } -/* - * query AMP capabilities for the given widget and direction +/** + * query_amp_caps - query AMP capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * + * Query AMP capabilities for the given widget and direction. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { @@ -1189,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) } EXPORT_SYMBOL_HDA(query_amp_caps); +/** + * snd_hda_override_amp_caps - Override the AMP capabilities + * @codec: the CODEC to clean up + * @nid: the NID to clean up + * @direction: either #HDA_INPUT or #HDA_OUTPUT + * @caps: the capability bits to set + * + * Override the cached AMP caps bits value by the given one. + * This function is useful if the driver needs to adjust the AMP ranges, + * e.g. limit to 0dB, etc. + * + * Returns zero if successful or a negative error code. + */ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { @@ -1224,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), @@ -1271,8 +1357,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, info->vol[ch] = val; } -/* - * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. +/** + * snd_hda_codec_amp_read - Read AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * + * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) @@ -1285,8 +1378,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); -/* - * update the AMP value, mask = bit mask to set, val = the value +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. */ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) @@ -1305,8 +1408,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); -/* - * update the AMP stereo with the same mask and value +/** + * snd_hda_codec_amp_stereo - update the AMP stereo values + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP values like snd_hda_codec_amp_update(), but for a + * stereo widget with the same mask and value. */ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int direction, int idx, int mask, int val) @@ -1320,7 +1432,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); #ifdef SND_HDA_NEEDS_RESUME -/* resume the all amp commands from the cache */ +/** + * snd_hda_codec_resume_amp - Resume all AMP commands from the cache + * @codec: HD-audio codec + * + * Resume the all amp commands from the cache. + */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { struct hda_amp_info *buffer = codec->amp_cache.buf.list; @@ -1346,7 +1463,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ -/* volume */ +/** + * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1402,6 +1524,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, HDA_AMP_VOLMASK, val); } +/** + * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1421,6 +1549,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); +/** + * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1445,6 +1579,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); +/** + * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) { @@ -1474,8 +1614,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); -/* - * set (static) TLV for virtual master volume; recalculated as max 0dB +/** + * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control + * @codec: HD-audio codec + * @nid: NID of a reference widget + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @tlv: TLV data to be stored, at least 4 elements + * + * Set (static) TLV data for a virtual master volume using the AMP caps + * obtained from the reference NID. + * The volume range is recalculated as if the max volume is 0dB. */ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv) @@ -1509,6 +1657,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec, return snd_ctl_find_id(codec->bus->card, &id); } +/** + * snd_hda_find_mixer_ctl - Find a mixer control element with the given name + * @codec: HD-audio codec + * @name: ctl id name string + * + * Get the control element with the given id string and IFACE_MIXER. + */ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name) { @@ -1516,7 +1671,24 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); -/* Add a control element and assign to the codec */ +/** + * snd_hda_ctl-add - Add a control element and assign to the codec + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * + * Add the given control element to an array inside the codec instance. + * All control elements belonging to a codec are supposed to be added + * by this function so that a proper clean-up works at the free or + * reconfiguration time. + * + * If non-zero @nid is passed, the NID is assigned to the control element. + * The assignment is shown in the codec proc file. + * + * snd_hda_ctl_add() checks the control subdev id field whether + * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower + * bits value is taken as the NID to assign. + */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { @@ -1540,7 +1712,10 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); -/* Clear all controls assigned to the given codec */ +/** + * snd_hda_ctls_clear - Clear all controls assigned to the given codec + * @codec: HD-audio codec + */ void snd_hda_ctls_clear(struct hda_codec *codec) { int i; @@ -1572,6 +1747,16 @@ static void hda_unlock_devices(struct snd_card *card) spin_unlock(&card->files_lock); } +/** + * snd_hda_codec_reset - Clear all objects assigned to the codec + * @codec: HD-audio codec + * + * This frees the all PCM and control elements assigned to the codec, and + * clears the caches and restores the pin default configurations. + * + * When a device is being used, it returns -EBSY. If successfully freed, + * returns zero. + */ int snd_hda_codec_reset(struct hda_codec *codec) { struct snd_card *card = codec->bus->card; @@ -1635,7 +1820,22 @@ int snd_hda_codec_reset(struct hda_codec *codec) return 0; } -/* create a virtual master control and add slaves */ +/** + * snd_hda_add_vmaster - create a virtual master control and add slaves + * @codec: HD-audio codec + * @name: vmaster control name + * @tlv: TLV data (optional) + * @slaves: slave control names (optional) + * + * Create a virtual master control with the given name. The TLV data + * must be either NULL or a valid data. + * + * @slaves is a NULL-terminated array of strings, each of which is a + * slave control name. All controls with these names are assigned to + * the new virtual master control. + * + * This function returns zero if successful or a negative error code. + */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves) { @@ -1677,7 +1877,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, } EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); -/* switch */ +/** + * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1691,6 +1896,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); +/** + * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1711,6 +1922,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); +/** + * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch + * + * The control element is supposed to have the private_value field + * set up via HDA_COMPOSE_AMP_VAL*() or related macros. + */ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1742,6 +1959,12 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +/** + * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch + * + * This function calls snd_hda_enable_beep_device(), which behaves differently + * depending on beep_mode option. + */ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1762,6 +1985,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) +/** + * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1779,6 +2008,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); +/** + * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_MUTE*() macros. + */ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1803,8 +2038,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); -/* - * generic bound volume/swtich controls +/** + * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. */ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1823,6 +2061,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); +/** + * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1840,6 +2084,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); +/** + * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. + */ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1863,6 +2113,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); +/** + * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control + * + * The control element is supposed to have the private_value field + * set up via HDA_BIND_VOL() macro. + */ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { @@ -2185,6 +2441,11 @@ static struct snd_kcontrol_new spdif_share_sw = { .put = spdif_share_sw_put, }; +/** + * snd_hda_create_spdif_share_sw - create Default PCM switch + * @codec: the HDA codec + * @mout: multi-out instance + */ int snd_hda_create_spdif_share_sw(struct hda_codec *codec, struct hda_multi_out *mout) { @@ -2352,7 +2613,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); -/* resume the all commands from the cache */ +/** + * snd_hda_codec_resume_cache - Resume the all commands from the cache + * @codec: HD-audio codec + * + * Execute all verbs recorded in the command caches to resume. + */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { struct hda_cache_head *buffer = codec->cmd_cache.buf.list; @@ -2778,8 +3044,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, } /** - * snd_hda_is_supported_format - check whether the given node supports - * the format val + * snd_hda_is_supported_format - Check the validity of the format + * @codec: HD-audio codec + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. * * Returns 1 if supported, 0 if not. */ @@ -2899,6 +3169,7 @@ static int set_pcm_default_values(struct hda_codec *codec, return 0; } +/* global */ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { "Audio", "SPDIF", "HDMI", "Modem" }; @@ -3216,6 +3487,7 @@ static void hda_keep_power_on(struct hda_codec *codec) codec->power_jiffies = jiffies; } +/* update the power on/off account with the current jiffies */ void snd_hda_update_power_acct(struct hda_codec *codec) { unsigned long delta = jiffies - codec->power_jiffies; @@ -3226,6 +3498,13 @@ void snd_hda_update_power_acct(struct hda_codec *codec) codec->power_jiffies += delta; } +/** + * snd_hda_power_up - Power-up the codec + * @codec: HD-audio codec + * + * Increment the power-up counter and power up the hardware really when + * not turned on yet. + */ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; @@ -3248,9 +3527,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); #define power_save(codec) \ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) -#define power_save(codec) \ - ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) - +/** + * snd_hda_power_down - Power-down the codec + * @codec: HD-audio codec + * + * Decrement the power-up counter and schedules the power-off work if + * the counter rearches to zero. + */ void snd_hda_power_down(struct hda_codec *codec) { --codec->power_count; @@ -3264,6 +3547,19 @@ void snd_hda_power_down(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_power_down); +/** + * snd_hda_check_amp_list_power - Check the amp list and update the power + * @codec: HD-audio codec + * @check: the object containing an AMP list and the status + * @nid: NID to check / update + * + * Check whether the given NID is in the amp list. If it's in the list, + * check the current AMP status, and update the the power-status according + * to the mute status. + * + * This function is supposed to be set or called from the check_power_status + * patch ops. + */ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid) @@ -3305,6 +3601,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); /* * Channel mode helper */ + +/** + * snd_hda_ch_mode_info - Info callback helper for the channel mode enum + */ int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, const struct hda_channel_mode *chmode, @@ -3321,6 +3621,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); +/** + * snd_hda_ch_mode_get - Get callback helper for the channel mode enum + */ int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3339,6 +3642,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); +/** + * snd_hda_ch_mode_put - Put callback helper for the channel mode enum + */ int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, const struct hda_channel_mode *chmode, @@ -3363,6 +3669,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); /* * input MUX helper */ + +/** + * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum + */ int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo) { @@ -3381,6 +3691,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, } EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); +/** + * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum + */ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, @@ -3440,7 +3753,10 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } } -/* call each reboot notifier */ +/** + * snd_hda_bus_reboot_notify - call the reboot notifier of each codec + * @bus: HD-audio bus + */ void snd_hda_bus_reboot_notify(struct hda_bus *bus) { struct hda_codec *codec; @@ -3458,8 +3774,8 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) } EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify); -/* - * open the digital out in the exclusive mode +/** + * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode */ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3474,6 +3790,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); +/** + * snd_hda_multi_out_dig_prepare - prepare the digital out stream + */ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, @@ -3487,6 +3806,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); +/** + * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream + */ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) { @@ -3497,8 +3819,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup); -/* - * release the digital out +/** + * snd_hda_multi_out_dig_close - release the digital out stream */ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) @@ -3510,8 +3832,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); -/* - * set up more restrictions for analog out +/** + * snd_hda_multi_out_analog_open - open analog outputs + * + * Open analog outputs and set up the hw-constraints. + * If the digital outputs can be opened as slave, open the digital + * outputs, too. */ int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3556,9 +3882,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); -/* - * set up the i/o for analog out - * when the digital out is available, copy the front out to digital out, too. +/** + * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. + * + * Set up the i/o for analog out. + * When the digital out is available, copy the front out to digital out, too. */ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -3615,8 +3943,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); -/* - * clean up the setting for analog out +/** + * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out */ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -4002,8 +4330,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume); * generic arrays */ -/* get a new element from the given array - * if it exceeds the pre-allocated array size, re-allocate the array +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. */ void *snd_array_new(struct snd_array *array) { @@ -4027,7 +4361,10 @@ void *snd_array_new(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_new); -/* free the given array elements */ +/** + * snd_array_free - free the given array elements + * @array: the array object + */ void snd_array_free(struct snd_array *array) { kfree(array->list); @@ -4037,7 +4374,12 @@ void snd_array_free(struct snd_array *array) } EXPORT_SYMBOL_HDA(snd_array_free); -/* +/** + * snd_print_pcm_rates - Print the supported PCM rates to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * * used by hda_proc.c and hda_eld.c */ void snd_print_pcm_rates(int pcm, char *buf, int buflen) @@ -4056,6 +4398,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen) } EXPORT_SYMBOL_HDA(snd_print_pcm_rates); +/** + * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer + * @pcm: PCM caps bits + * @buf: the string buffer to write + * @buflen: the max buffer length + * + * used by hda_proc.c and hda_eld.c + */ void snd_print_pcm_bits(int pcm, char *buf, int buflen) { static unsigned int bits[] = { 8, 16, 20, 24, 32 }; -- cgit v1.2.2 From 9bb1fe390de3e1def0dd162dbdaf62e0981105fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 15:33:49 +0100 Subject: ALSA: hda - Fix beep_mode option value The beep_mode option value was wrongly defined: it must be 0 = off and 1 = on. Also, evaluate the beep_mode value at snd_hda_attach_beep_device() properly so that no device is created when beep_mode=0 is given. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 4 +++- sound/pci/hda/hda_beep.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 9e48798b415b..5fe34a8d8c81 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -215,7 +215,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) struct hda_beep *beep; if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly */ + return 0; /* disabled explicitly by hints */ + if (codec->beep_mode == HDA_BEEP_MODE_OFF) + return 0; /* disabled by module option */ beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (beep == NULL) diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 17dd1c325e32..f1de1bac042c 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -24,8 +24,8 @@ #include "hda_codec.h" -#define HDA_BEEP_MODE_ON 0 -#define HDA_BEEP_MODE_OFF 1 +#define HDA_BEEP_MODE_OFF 0 +#define HDA_BEEP_MODE_ON 1 #define HDA_BEEP_MODE_SWREG 2 /* beep information */ -- cgit v1.2.2 From 67d634c07afd8f70973d925463e775fdb89ad536 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 16 Nov 2009 15:35:59 +0100 Subject: ALSA: hda - Fix build errors with CONFIG_SND_HDA_INPUT_BEEP=n Disable beep-related codes when CONFIG_SND_HDA_INPUT_BEEP isn't set. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/hda_local.h | 8 ++++++++ sound/pci/hda/patch_analog.c | 6 ++++++ sound/pci/hda/patch_realtek.c | 8 ++++++++ 4 files changed, 24 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e344235da491..2be61b31fb3c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1959,6 +1959,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /** * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch * @@ -1975,6 +1976,7 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep); +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ /* * bound volume controls diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7c049839ea26..d4a3d0942c00 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -77,6 +77,7 @@ /* stereo mute switch */ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ @@ -85,6 +86,11 @@ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } +#else +/* no digital beep - just the standard one */ +#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \ + HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) +#endif /* CONFIG_SND_HDA_INPUT_BEEP */ /* special beep mono mute switch */ #define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \ HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction) @@ -108,8 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#ifdef CONFIG_SND_HDA_INPUT_BEEP int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#endif /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index ceb0c603da04..8a1064bdf4c6 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -156,6 +156,7 @@ static const char *ad_slave_sws[] = { static void ad198x_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), @@ -165,6 +166,9 @@ static struct snd_kcontrol_new ad_beep_mixer[] = { #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif static int ad198x_build_controls(struct hda_codec *codec) { @@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->beep_amp) { struct snd_kcontrol_new *knew; for (knew = ad_beep_mixer; knew->name; knew++) { @@ -209,6 +214,7 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } } +#endif /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eee3143eef75..ef7d21097eeb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2410,12 +2410,14 @@ static const char *alc_slave_sws[] = { static void alc_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new alc_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), { } /* end */ }; +#endif static int alc_build_controls(struct hda_codec *codec) { @@ -2452,6 +2454,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* create beep controls if needed */ if (spec->beep_amp) { struct snd_kcontrol_new *knew; @@ -2467,6 +2470,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } } +#endif /* if we have no master control, let's create it */ if (!spec->no_analog && @@ -4780,8 +4784,12 @@ static void set_capture_mixer(struct hda_codec *codec) } } +#ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif /* * OK, here we have finally the patch for ALC880 -- cgit v1.2.2 From c5b5165ce28099484d5fa733abeae48540680440 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 17 Nov 2009 16:01:58 +0100 Subject: ALSA: hda - Disable default quirk for Sony VAIO with ALC262 codec The ALC262 has a quirk entry matching with all Sony Vaio laptops to use model=sony-assamd as default. But, model=auto works much better for new models in the recent driver versions, thus it's safer to disable that default quirk entry. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ba339d745aab..578420523606 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -11471,8 +11471,10 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06), SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO), SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO), +#if 0 /* disable the quirk since model=auto works better in recent versions */ SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO", ALC262_SONY_ASSAMD), +#endif SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", ALC262_TOSHIBA_RX1), SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), -- cgit v1.2.2 From 6f539a98614a014a7d6b64ab62b0dddb14e2d8cc Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:37:59 +0800 Subject: ALSA: intelhdmi - fix audio infoframe fill size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 4f25f08d332b..ad1aa5d87dda 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -509,12 +509,12 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec, hdmi_debug_dip_size(codec, pin_nid); hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - for (i = 0; i < sizeof(ai); i++) + for (i = 0; i < sizeof(*ai); i++) sum += params[i]; ai->checksum = - sum; hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(ai); i++) + for (i = 0; i < sizeof(*ai); i++) hdmi_write_dip_byte(codec, pin_nid, params[i]); } -- cgit v1.2.2 From 1e7c10fefadb42d9300305c7de57bea365855e9b Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:00 +0800 Subject: ALSA: intelhdmi - fix channel mapping slot mask Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index ad1aa5d87dda..82312c67f8dd 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -433,7 +433,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) slot = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0x7); + slot >> 4, slot & 0xf); } #endif } -- cgit v1.2.2 From 23ccc2bd246a5bdb1ac03dc9040a0585c1890ef3 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:01 +0800 Subject: ALSA: intelhdmi - export monitor-presence and ELD-valid status Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_eld.c | 8 +++++++- sound/pci/hda/hda_local.h | 4 +++- sound/pci/hda/patch_intelhdmi.c | 8 +++----- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 20fa6aee29c0..de50cfcf644e 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -477,6 +477,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; + snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present); + snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid); snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); @@ -518,7 +520,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, * monitor_name manufacture_id product_id * eld_version edid_version */ - if (!strcmp(name, "connection_type")) + if (!strcmp(name, "monitor_present")) + e->monitor_present = val; + else if (!strcmp(name, "eld_valid")) + e->eld_valid = val; + else if (!strcmp(name, "connection_type")) e->conn_type = val; else if (!strcmp(name, "port_id")) e->port_id = val; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index d4a3d0942c00..070b74384d43 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -569,9 +569,11 @@ struct cea_sad { * ELD: EDID Like Data */ struct hdmi_eld { + bool monitor_present; + bool eld_valid; int eld_size; int baseline_len; - int eld_ver; /* (eld_ver == 0) indicates invalid ELD */ + int eld_ver; int cea_edid_ver; char monitor_name[ELD_MAX_MNL + 1]; int manufacture_id; diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 82312c67f8dd..095c993f4b76 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -62,8 +62,6 @@ struct intel_hdmi_spec { /* * HDMI sink attached to each pin */ - bool sink_present[INTEL_HDMI_PINS]; - bool sink_eldv[INTEL_HDMI_PINS]; struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; /* @@ -645,7 +643,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < spec->num_pins; i++) { if (spec->pin_cvt[i] != nid) continue; - if (spec->sink_present[i] != true) + if (!spec->sink_eld[i].monitor_present) continue; pin_nid = spec->pin[i]; @@ -675,8 +673,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) if (index < 0) return; - spec->sink_present[index] = pind; - spec->sink_eldv[index] = eldv; + spec->sink_eld[index].monitor_present = pind; + spec->sink_eld[index].eld_valid = eldv; if (pind && eldv) { hdmi_parse_eld(codec, index); -- cgit v1.2.2 From 864f92be7e8d4a0ba11d912e3f03d1a92a031dee Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:02 +0800 Subject: ALSA: hda - introduce snd_hda_jack_detect() and snd_hda_pin_sense() This helps merge duplicate code. v2: add snd_hda_jack_detect() and comments recommended by Takashi. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 34 +++++++ sound/pci/hda/hda_eld.c | 7 +- sound/pci/hda/hda_local.h | 2 + sound/pci/hda/patch_cirrus.c | 19 +--- sound/pci/hda/patch_realtek.c | 206 ++++++++++-------------------------------- 5 files changed, 91 insertions(+), 177 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2be61b31fb3c..9cfdb771928c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1317,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); +/** + * snd_hda_pin_sense - execute pin sense measurement + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Execute necessary pin sense measurement and return its Presence Detect, + * Impedance, ELD Valid etc. status bits. + */ +u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + u32 pincap = snd_hda_query_pin_caps(codec, nid); + + if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + + return snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0); +} +EXPORT_SYMBOL_HDA(snd_hda_pin_sense); + +/** + * snd_hda_jack_detect - query pin Presence Detect status + * @codec: the CODEC to sense + * @nid: the pin NID to sense + * + * Query and return the pin's Presence Detect status. + */ +int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + u32 sense = snd_hda_pin_sense(codec, nid); + return !!(sense & AC_PINSENSE_PRESENCE); +} +EXPORT_SYMBOL_HDA(snd_hda_jack_detect); + /* * read the current volume to info * if the cache exists, read the cache value. diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index de50cfcf644e..4228f2fe5956 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -309,17 +309,12 @@ out_fail: return -EINVAL; } -static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); -} - static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid) { int eldv; int present; - present = hdmi_present_sense(codec, nid); + present = snd_hda_pin_sense(codec, nid); eldv = (present & AC_PINSENSE_ELDV); present = (present & AC_PINSENSE_PRESENCE); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 070b74384d43..5778ae882b83 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -461,6 +461,8 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); +u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); +int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); struct hda_nid_item { struct snd_kcontrol *kctl; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 9ac09e4568b3..2439e84dcb21 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int caps, present, hp_present; + unsigned int caps, hp_present; hda_nid_t nid; int i; @@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec) caps = snd_hda_query_pin_caps(codec, nid); if (!(caps & AC_PINCAP_PRES_DETECT)) continue; - if (caps & AC_PINCAP_TRIG_REQ) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - hp_present |= (present & AC_PINSENSE_PRESENCE) != 0; + hp_present = snd_hda_jack_detect(codec, nid); if (hp_present) break; } @@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec) struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; - unsigned int caps, present; + unsigned int present; nid = cfg->input_pins[spec->automic_idx]; - caps = snd_hda_query_pin_caps(codec, nid); - if (caps & AC_PINCAP_TRIG_REQ) - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (present & AC_PINSENSE_PRESENCE) + present = snd_hda_jack_detect(codec, nid); + if (present) change_cur_input(codec, spec->automic_idx, 0); else { unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ? diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 578420523606..cbb2d326e6ad 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -961,18 +961,12 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, static void alc_automute_pin(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present, pincap; unsigned int nid = spec->autocfg.hp_pins[0]; int i; if (!nid) return; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, nid); for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { nid = spec->autocfg.speaker_pins[i]; if (!nid) @@ -1012,9 +1006,7 @@ static void alc_mic_automute(struct hda_codec *codec) cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; - present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0, - AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, spec->ext_mic.pin); if (present) { alive = &spec->ext_mic; dead = &spec->int_mic; @@ -1513,7 +1505,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { static void alc_automute_amp(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int val, mute, pincap; + unsigned int mute; hda_nid_t nid; int i; @@ -1522,13 +1514,7 @@ static void alc_automute_amp(struct hda_codec *codec) nid = spec->autocfg.hp_pins[i]; if (!nid) break; - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_SENSE, 0); - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (val & AC_PINSENSE_PRESENCE) { + if (snd_hda_jack_detect(codec, nid)) { spec->jack_present = 1; break; } @@ -2784,8 +2770,7 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x18); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } @@ -5102,11 +5087,8 @@ static struct hda_verb alc260_hp_unsol_verbs[] = { static void alc260_hp_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x10, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x10); alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); } @@ -5171,11 +5153,8 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = { static void alc260_hp_3013_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x15); alc260_hp_master_update(codec, 0x15, 0x10, 0x11); } @@ -5188,12 +5167,8 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec, static void alc260_hp_3012_automute(struct hda_codec *codec) { - unsigned int present, bits; + unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT; - present = snd_hda_codec_read(codec, 0x10, 0, - AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; - - bits = present ? 0 : PIN_OUT; snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, bits); snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, @@ -5763,8 +5738,7 @@ static void alc260_replacer_672v_automute(struct hda_codec *codec) unsigned int present; /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ - present = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x0f); if (present) { snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 1); @@ -8196,12 +8170,8 @@ static void alc883_mitac_setup(struct hda_codec *codec) /* static void alc883_mitac_mic_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } */ @@ -8423,10 +8393,8 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = { /* toggle front-jack and RCA according to the hp-jack state */ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x1b); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, @@ -8436,10 +8404,8 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) /* toggle RCA according to the front-jack state */ static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x14); - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -8532,24 +8498,16 @@ static void alc883_haier_w66_setup(struct hda_codec *codec) static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0; - 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_lenovo_101e_all_automute(struct hda_codec *codec) { - unsigned int present; - unsigned char bits; + int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -8700,8 +8658,7 @@ static void alc889A_mb31_automute(struct hda_codec *codec) /* Mute only in 2ch or 4ch mode */ if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0) == 0x00) { - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x15); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, @@ -10044,10 +10001,8 @@ static void alc262_hp_master_update(struct hda_codec *codec) static void alc262_hp_bpc_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int presence; - presence = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + + spec->jack_present = snd_hda_jack_detect(codec, 0x1b); alc262_hp_master_update(codec); } @@ -10061,10 +10016,8 @@ static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res) static void alc262_hp_wildwest_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int presence; - presence = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + + spec->jack_present = snd_hda_jack_detect(codec, 0x15); alc262_hp_master_update(codec); } @@ -10298,13 +10251,8 @@ static void alc262_hippo_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, hp_nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + spec->jack_present = snd_hda_jack_detect(codec, hp_nid); alc262_hippo_master_update(codec); } @@ -10630,21 +10578,8 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); - /* check laptop HP jack */ - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - /* check docking HP jack */ - present |= snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - if (present & AC_PINSENSE_PRESENCE) - spec->jack_present = 1; - else - spec->jack_present = 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x14) || + snd_hda_jack_detect(codec, 0x1b); spec->sense_updated = 1; } /* unmute internal speaker only if both HPs are unplugged and @@ -10689,12 +10624,7 @@ static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force) 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->jack_present = snd_hda_jack_detect(codec, 0x1b); spec->sense_updated = 1; } if (spec->jack_present) { @@ -10886,12 +10816,7 @@ static void alc262_ultra_automute(struct hda_codec *codec) 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; + spec->jack_present = snd_hda_jack_detect(codec, 0x15); if (spec->jack_present) mute = HDA_AMP_MUTE; } @@ -11933,10 +11858,7 @@ static void alc268_acer_automute(struct hda_codec *codec, int force) unsigned int mute; if (force || !spec->sense_updated) { - unsigned int present; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; + spec->jack_present = snd_hda_jack_detect(codec, 0x14); spec->sense_updated = 1; } if (spec->jack_present) @@ -12055,8 +11977,7 @@ static void alc268_aspire_one_speaker_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) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -13039,8 +12960,7 @@ static void alc269_quanta_fl1_speaker_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) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -13065,12 +12985,10 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec) unsigned char bits; /* Check laptop headphone socket */ - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); /* Check port replicator headphone socket */ - present |= snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present |= snd_hda_jack_detect(codec, 0x1a); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, @@ -13094,11 +13012,8 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec) unsigned int present_laptop; unsigned int present_dock; - present_laptop = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - - present_dock = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present_laptop = snd_hda_jack_detect(codec, 0x18); + present_dock = snd_hda_jack_detect(codec, 0x1b); /* Laptop mic port overrides dock mic port, design decision */ if (present_dock) @@ -13183,8 +13098,7 @@ static void alc269_speaker_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) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -14162,10 +14076,8 @@ static struct hda_verb alc861_toshiba_init_verbs[] = { /* toggle speaker-output according to the hp-jack state */ static void alc861_toshiba_automute(struct hda_codec *codec) { - unsigned int present; + unsigned int present = snd_hda_jack_detect(codec, 0x0f); - present = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, @@ -15070,9 +14982,9 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x18); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } @@ -16383,9 +16295,9 @@ static void alc662_lenovo_101e_ispeaker_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) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x14); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); } @@ -16395,9 +16307,9 @@ static void alc662_lenovo_101e_all_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1b); bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -16456,9 +16368,7 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16471,9 +16381,7 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16490,9 +16398,7 @@ static void alc663_15jd_two_speaker_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; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -16509,9 +16415,7 @@ static void alc662_f5z_speaker_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x1b); bits = present ? 0 : PIN_OUT; snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, bits); @@ -16521,12 +16425,8 @@ static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec) { unsigned int present1, present2; - present1 = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - present2 = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present1 = snd_hda_jack_detect(codec, 0x21); + present2 = snd_hda_jack_detect(codec, 0x15); if (present1 || present2) { snd_hda_codec_write_cache(codec, 0x14, 0, @@ -16541,12 +16441,8 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) { unsigned int present1, present2; - present1 = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; - present2 = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present1 = snd_hda_jack_detect(codec, 0x1b); + present2 = snd_hda_jack_detect(codec, 0x15); if (present1 || present2) { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, @@ -16706,9 +16602,7 @@ static void alc663_g71v_hp_automute(struct hda_codec *codec) unsigned int present; unsigned char bits; - present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); @@ -16721,9 +16615,7 @@ static void alc663_g71v_front_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; + present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits); -- cgit v1.2.2 From 3f54aa5091f48e9d8ce6e99b248449d08acccb26 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:03 +0800 Subject: ALSA: intelhdmi - probe for monitor/eld presence at module init time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids lost of presence info on module reloading. The presence info used to be only updated at the (rare) hotplug events. Proposed by David, thanks! CC: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 095c993f4b76..c5fd011567fb 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -259,6 +259,25 @@ static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) return 0; } +static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + snd_hdmi_show_eld(eld); +} + +static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + int present = snd_hda_pin_sense(codec, pin_nid); + + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + + if (present & AC_PINSENSE_ELDV) + hdmi_get_show_eld(codec, pin_nid, eld); +} + static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) { struct intel_hdmi_spec *spec = codec->spec; @@ -269,6 +288,8 @@ static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } + hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + spec->pin[spec->num_pins] = pin_nid; spec->num_pins++; @@ -436,15 +457,6 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) #endif } -static void hdmi_parse_eld(struct hda_codec *codec, int index) -{ - struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld = &spec->sink_eld[index]; - - if (!snd_hdmi_get_eld(eld, codec, spec->pin[index])) - snd_hdmi_show_eld(eld); -} - /* * Audio InfoFrame routines @@ -677,7 +689,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) spec->sink_eld[index].eld_valid = eldv; if (pind && eldv) { - hdmi_parse_eld(codec, index); + hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]); /* TODO: do real things about ELD */ } } -- cgit v1.2.2 From 978be6d711be237e0344eca21c3922ae88a240bc Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:04 +0800 Subject: ALSA: intelhdmi - separate out infoframe checksum routine And make it right when called for more than one times. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index c5fd011567fb..d68dba9ac113 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -508,24 +508,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) #endif } +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 sum = 0; + int i; + + ai->checksum = 0; + + for (i = 0; i < sizeof(*ai); i++) + sum += bytes[i]; + + ai->checksum = - sum; +} + static void hdmi_fill_audio_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, struct hdmi_audio_infoframe *ai) { - u8 *params = (u8 *)ai; - u8 sum = 0; + u8 *bytes = (u8 *)ai; int i; hdmi_debug_dip_size(codec, pin_nid); hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - for (i = 0; i < sizeof(*ai); i++) - sum += params[i]; - ai->checksum = - sum; + hdmi_checksum_audio_infoframe(ai); hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < sizeof(*ai); i++) - hdmi_write_dip_byte(codec, pin_nid, params[i]); + hdmi_write_dip_byte(codec, pin_nid, bytes[i]); } /* -- cgit v1.2.2 From 848de598eef9603d6f2c174f90fded4e63ac5e23 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:05 +0800 Subject: ALSA: intelhdmi - sticky infoframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remember the active infoframe, so as to avoid stop/restart infoframe transmission when switching between audio clips of the same format. Proposed by Shang and David. CC: Shane W CC: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index d68dba9ac113..abb056fde67a 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -646,6 +646,27 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, hdmi_debug_channel_mapping(codec, nid); } +static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 val; + int i; + + if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) + != AC_DIPXMIT_BEST) + return false; + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) { + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_DATA, 0); + if (val != bytes[i]) + return false; + } + + return true; +} static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, struct snd_pcm_substream *substream) @@ -670,8 +691,11 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, continue; pin_nid = spec->pin[i]; - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); + if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } } } @@ -767,16 +791,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct intel_hdmi_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != hinfo->nid) - continue; - - hdmi_stop_infoframe_trans(codec, spec->pin[i]); - } - snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v1.2.2 From 5779191e0efd851fb0d54698c13cb4f5325caca6 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:06 +0800 Subject: ALSA: intelhdmi - sticky stream id and format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We tracked down the first-0.5s-hdmi-audio-samples-lost problem to the AC_VERB_SET_CHANNEL_STREAMID command. It is suspected that many HDMI sinks need some time to adapt to the new state. The workaround is to avoid changing stream id/format whenever possible. Proposed by David. Signed-off-by: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index abb056fde67a..8a1cf9d7e5ce 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -772,6 +772,31 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) * Callbacks */ +static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, int format) +{ + int tag; + int fmt; + + tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; + fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); + + snd_printdd("hdmi_setup_stream: " + "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", + nid, + tag == stream_tag ? "" : "new-", + stream_tag, + fmt == format ? "" : "new-", + format); + + if (tag != stream_tag) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4); + if (fmt != format) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); +} + static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -783,7 +808,7 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); - snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); return 0; } @@ -791,7 +816,6 @@ static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - snd_hda_codec_cleanup_stream(codec, hinfo->nid); return 0; } -- cgit v1.2.2 From 81bf31e2d0a6a9f5d83da0a757f8ca03db908162 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:07 +0800 Subject: ALSA: intelhdmi - sticky channel count Don't change channel count if not necessary. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 8a1cf9d7e5ce..928df59be5d8 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -422,24 +422,18 @@ static void hdmi_stop_infoframe_trans(struct hda_codec *codec, AC_DIPXMIT_DISABLE); } -#ifdef CONFIG_SND_DEBUG_VERBOSE static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) { return 1 + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CVT_CHAN_COUNT, 0); } -#endif static void hdmi_set_channel_count(struct hda_codec *codec, hda_nid_t nid, int chs) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); - -#ifdef CONFIG_SND_DEBUG_VERBOSE if (chs != hdmi_get_channel_count(codec, nid)) - snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", - chs, hdmi_get_channel_count(codec, nid)); -#endif + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) -- cgit v1.2.2 From 83d605fd63e704419ccb92d48b735c6890ce3d6a Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 18 Nov 2009 12:38:08 +0800 Subject: ALSA: hda - show EPSS capability in proc Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_proc.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index be6c5f443cd9..2d627613aea3 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -286,6 +286,10 @@ enum { #define AC_PWRST_D1SUP (1<<1) #define AC_PWRST_D2SUP (1<<2) #define AC_PWRST_D3SUP (1<<3) +#define AC_PWRST_D3COLDSUP (1<<4) +#define AC_PWRST_S3D3COLDSUP (1<<29) +#define AC_PWRST_CLKSTOP (1<<30) +#define AC_PWRST_EPSS (1U<<31) /* Power state values */ #define AC_PWRST_SETTING (0xf<<0) diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index f465cff28041..09476fc1ab64 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -26,6 +26,21 @@ #include "hda_codec.h" #include "hda_local.h" +static char *bits_names(unsigned int bits, char *names[], int size) +{ + int i, n; + static char buf[128]; + + for (i = 0, n = 0; i < size; i++) { + if (bits & (1U<> -- cgit v1.2.2 From d56757abc11a21996d9839c0d4e3b2c3666cd318 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 08:00:14 +0100 Subject: ALSA: hda - Replace the rest of jack-detections with snd_hda_jack_detect() Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 47 ++++++++++++++---------------------------- sound/pci/hda/patch_conexant.c | 37 ++++++++++----------------------- sound/pci/hda/patch_realtek.c | 3 +-- sound/pci/hda/patch_sigmatel.c | 7 ++----- sound/pci/hda/patch_via.c | 46 ++++++++++++++--------------------------- 5 files changed, 45 insertions(+), 95 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8a1064bdf4c6..455a0494f907 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -720,10 +720,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { 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); + present = snd_hda_jack_detect(codec, 0x1f); /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - (present & AC_PINSENSE_PRESENCE) ? 0 : 2); + present ? 0 : 2); } #define AD1986A_MIC_EVENT 0x36 @@ -762,10 +762,8 @@ static void ad1986a_update_hp(struct hda_codec *codec) static void ad1986a_hp_automute(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = !!(present & 0x80000000); + spec->jack_present = snd_hda_jack_detect(codec, 0x1a); if (spec->inv_jack_detect) spec->jack_present = !spec->jack_present; ad1986a_update_hp(codec); @@ -1555,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x06, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x06); snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -1576,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x08, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x08); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -2532,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) { if ((res >> 26) != AD1988_HP_EVENT) return; - if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31)) + if (snd_hda_jack_detect(codec, 0x11)) snd_hda_sequence_write(codec, ad1988_laptop_hp_on); else snd_hda_sequence_write(codec, ad1988_laptop_hp_off); @@ -3778,8 +3774,7 @@ 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; + present = snd_hda_jack_detect(codec, 0x11); 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, @@ -3791,8 +3786,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x14); snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, present ? 0 : 1); } @@ -3827,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; - if (!present) { - present = snd_hda_codec_read(codec, 0x12, 0, - AC_VERB_GET_PIN_SENSE, 0); - present &= AC_PINSENSE_PRESENCE; - } + present = snd_hda_jack_detect(codec, 0x11); + if (!present) + present = snd_hda_jack_detect(codec, 0x12); 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, @@ -3845,11 +3835,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec) { unsigned int idx; - if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE) + if (snd_hda_jack_detect(codec, 0x14)) idx = 0; - else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE) + else if (snd_hda_jack_detect(codec, 0x1c)) idx = 4; else idx = 1; @@ -4018,8 +4006,7 @@ 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; + present = snd_hda_jack_detect(codec, 0x11); snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -4127,14 +4114,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* switch to external mic if plugged */ static void ad1984a_touchsmart_automic(struct hda_codec *codec) { - if (snd_hda_codec_read(codec, 0x1c, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) { + if (snd_hda_jack_detect(codec, 0x1c)) snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, 0x4); - } else { + else snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, 0x5); - } } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 905859d4f4df..0b097fa5421f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -397,9 +397,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) for (i = 0; i < spec->jacks.used; i++) { if (jacks->nid == nid) { unsigned int present; - present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, nid); present = (present) ? jacks->type : 0 ; @@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x12, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x12); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int bits; - spec->hp_present = snd_hda_codec_read(codec, 0x11, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + spec->hp_present = snd_hda_jack_detect(codec, 0x11); bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, @@ -1243,8 +1239,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int bits; - spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + spec->hp_present = snd_hda_jack_detect(codec, 0x13); bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; /* See the note in cxt5047_hp_master_sw_put */ @@ -1267,8 +1262,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x15); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -1621,9 +1615,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec) if (spec->no_auto_mic) return; - present = snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x17); snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_CONNECT_SEL, present ? 0x01 : 0x00); @@ -1638,9 +1630,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) if (spec->no_auto_mic) return; - present = snd_hda_codec_read(codec, 0x18, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x18); if (present) spec->cur_adc_idx = 1; else @@ -1661,9 +1651,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, - AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE; + spec->hp_present = snd_hda_jack_detect(codec, 0x16); cxt5051_update_speaker(codec); } @@ -2011,8 +1999,7 @@ static void cxt5066_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x1a, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1a); if (present) { snd_printdd("CXT5066: external microphone detected\n"); snd_hda_sequence_write(codec, ext_mic_present); @@ -2029,12 +2016,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec) unsigned int portA, portD; /* Port A */ - portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + portA = snd_hda_jack_detect(codec, 0x19); /* Port D */ - portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE) << 1; + portD = snd_hda_jack_detect(codec, 0x1c); spec->hp_present = !!(portA | portD); snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cbb2d326e6ad..28acbe63dfc8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8446,8 +8446,7 @@ 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; + present = snd_hda_jack_detect(codec, 0x18); snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7f76a97954f9..d83649c25fb2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4413,14 +4413,11 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; - if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) - & (1 << 31)) - return 1; - return 0; + return snd_hda_jack_detect(codec, nid); } static void stac92xx_line_out_detect(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 0c621d74b165..b70e26ad263f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -547,8 +547,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned no_presence = (def_conf & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - unsigned present = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_SENSE, 0) >> 31; + unsigned present = snd_hda_jack_detect(codec, nid); struct via_spec *spec = codec->spec; if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) || ((no_presence || present) @@ -786,14 +785,11 @@ static void set_jack_power_state(struct hda_codec *codec) /* Mono out */ /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_codec_read( - codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1c); if (present) mono_out = 0; else { - present = snd_hda_codec_read( - codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0) - & 0x80000000; + present = snd_hda_jack_detect(codec, 0x1d); if (!spec->hp_independent_mode && present) mono_out = 0; else @@ -872,8 +868,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Class-D */ /* PW0 (24h), MW0(18h), MUX0(34h) */ - present = snd_hda_codec_read( - codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x25); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { @@ -894,8 +889,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Mono Out */ /* PW15 (31h), MW8(17h), MUX8(3bh) */ - present = snd_hda_codec_read( - codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x26); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { @@ -973,8 +967,7 @@ static void set_jack_power_state(struct hda_codec *codec) /* Internal Speaker */ /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_codec_read( - codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x25); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); if (present) { @@ -994,8 +987,7 @@ static void set_jack_power_state(struct hda_codec *codec) } /* Mono Out */ /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_codec_read( - codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x28); parm = AC_PWRST_D3; set_pin_power_state(codec, 0x31, &parm); if (present) { @@ -1920,8 +1912,7 @@ static void via_hp_automute(struct hda_codec *codec) unsigned int present = 0; struct via_spec *spec = codec->spec; - present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { struct snd_ctl_elem_id id; @@ -1947,9 +1938,8 @@ static void via_mono_automute(struct hda_codec *codec) if (spec->codec_type != VT1716S) return; - lineout_present = snd_hda_codec_read( - codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + lineout_present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); /* Mute Mono Out if Line Out is plugged */ if (lineout_present) { @@ -1958,9 +1948,7 @@ static void via_mono_automute(struct hda_codec *codec) return; } - hp_present = snd_hda_codec_read( - codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) snd_hda_codec_amp_stereo( @@ -2025,8 +2013,7 @@ static void via_speaker_automute(struct hda_codec *codec) if (spec->codec_type != VT2002P && spec->codec_type != VT1812) return; - hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); if (!spec->hp_independent_mode) { struct snd_ctl_elem_id id; @@ -2055,11 +2042,9 @@ static void via_hp_bind_automute(struct hda_codec *codec) if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) return; - hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); - present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); if (!spec->hp_independent_mode) { /* Mute Line-Outs */ @@ -2529,8 +2514,7 @@ static void vt1708_update_hp_jack_state(struct work_struct *work) return; /* if jack state toggled */ if (spec->vt1708_hp_present - != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_GET_PIN_SENSE, 0) >> 31)) { + != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { spec->vt1708_hp_present ^= 1; via_hp_automute(spec->codec); } -- cgit v1.2.2 From 8af3aeb498197f6fdf5acc913ffe8a392cb921c9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 14:23:37 +0100 Subject: ALSA: hda - Fix detection of dual headphones The dual-headphone mode with STAC/IDT codecs is useful only for machines that have two (or more) built-in headphones. But, some HP laptops give multiple headphone pin configs, one for the built-in and another for the separate (likely a docking station) one. This results in a missing speaker volume control. This patch adds more check for the dual-headphone mode to avoid this problem. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index d83649c25fb2..39001c47e627 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3635,6 +3635,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec) } } +static int is_dual_headphones(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i, valid_hps; + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT || + spec->autocfg.hp_outs <= 1) + return 0; + valid_hps = 0; + for (i = 0; i < spec->autocfg.hp_outs; i++) { + hda_nid_t nid = spec->autocfg.hp_pins[i]; + unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE) + continue; + valid_hps++; + } + return (valid_hps > 1); +} + + static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in) { struct sigmatel_spec *spec = codec->spec; @@ -3651,8 +3671,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out /* If we have no real line-out pin and multiple hp-outs, HPs should * be set up as multi-channel outputs. */ - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.hp_outs > 1) { + if (is_dual_headphones(codec)) { /* Copy hp_outs to line_outs, backup line_outs in * speaker_outs so that the following routines can handle * HP pins as primary outputs. -- cgit v1.2.2 From b4e818768d50a5b7aa1635676839682bcf0691b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Nov 2009 17:20:24 +0100 Subject: ALSA: hda - Fix mute-LED sync on HP laptops with IDT92HD83xxx codecs The mute-LED isn't synchronized with the actual mute state on some HP laptops with IDT 92HD83xxx codecs. A similar hack using check_power_status callback is added for this codec, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 39001c47e627..2a45375d79f8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -93,6 +93,7 @@ enum { STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, + STAC_92HD83XXX_HP, STAC_92HD83XXX_MODELS }; @@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", + [STAC_92HD83XXX_HP] = "hp", }; static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1634,6 +1636,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD83XXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, + "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; @@ -4834,6 +4838,23 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, return 0; } + +static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec, + hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + + if (nid != 0x13) + return 0; + if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) + spec->gpio_data |= spec->gpio_led; /* mute LED on */ + else + spec->gpio_data &= ~spec->gpio_led; /* mute LED off */ + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + + return 0; +} + #endif static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) @@ -5199,6 +5220,22 @@ again: break; } + codec->patch_ops = stac92xx_patch_ops; + + if (spec->board_config == STAC_92HD83XXX_HP) + spec->gpio_led = 0x01; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (spec->gpio_led) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + /* register check_power_status callback. */ + codec->patch_ops.check_power_status = + idt92hd83xxx_hp_check_power_status; + } +#endif + err = stac92xx_parse_auto_config(codec, 0x1d, 0); if (!err) { if (spec->board_config < 0) { @@ -5234,8 +5271,6 @@ again: snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, num_dacs); - codec->patch_ops = stac92xx_patch_ops; - codec->proc_widget_hook = stac92hd_proc_hook; return 0; -- cgit v1.2.2 From f2624791a0c2a2d7664b12d75ca327917141fd3b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Nov 2009 11:48:44 +0100 Subject: ALSA: hda - Change quirk for Acer Aspire 5930G Change the quirk for Acer Aspire 5930G from model=acer-aspire-4930g to model=acer-aspre-6530g. The tuba bass gets muted along with the other built-in speakers upon headphones insertion, the internal mic works perfectly etc. Reported-by: Claudio Viano Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 28acbe63dfc8..d29fa18232ad 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8754,7 +8754,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_4930G), + ALC888_ACER_ASPIRE_6530G), SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", -- cgit v1.2.2 From 7cef4cf1c5e9d81554137f52b96a5ab7f6241cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wojni=C5=82owicz?= Date: Fri, 20 Nov 2009 12:14:35 +0100 Subject: ALSA: hda - 4930g mute lfe and side when pluging in headphones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes first issue from comment 0021423 in bug 0004317 for Acer Aspire 5930g Signed-off-by: Łukasz Wojniłowicz Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d29fa18232ad..eedbe19306a0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1772,6 +1772,8 @@ static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; + spec->autocfg.speaker_pins[1] = 0x16; + spec->autocfg.speaker_pins[2] = 0x17; } static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec) -- cgit v1.2.2 From fc08722510494e8185e176713de8c47238512591 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 21 Nov 2009 19:57:11 +0100 Subject: ALSA: hda - Fix input and jack Kconfig depenencies CONFIG_SND_JACK needs to be selected explicitly only when INPUT=y or INPUT_SND. The current way, INPUT=SND_HDA_INTEL isn't strict enough. Reported-by: Randy Dunlap Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 25ae10e16f59..556cff937be7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -51,7 +51,7 @@ config SND_HDA_INPUT_BEEP_MODE config SND_HDA_INPUT_JACK bool "Support jack plugging notification via input layer" - depends on INPUT=y || INPUT=SND_HDA_INTEL + depends on INPUT=y || INPUT=SND select SND_JACK help Say Y here to enable the jack plugging notification via -- cgit v1.2.2 From 83dd7408b59c1945069199d712df8c7c64a76e1f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 24 Nov 2009 08:57:53 +0100 Subject: Revert "ALSA: hda - Change quirk for Acer Aspire 5930G" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f2624791a0c2a2d7664b12d75ca327917141fd3b. Łukasz Wojniłowicz reported that the change causes both internal and external mics not working any more. The headphone jacking issue was fixed by his previous patch, it's better to revert to acer-aspire-4930g model. Reported-by: Łukasz Wojniłowicz Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eedbe19306a0..7e8b17a1769a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8756,7 +8756,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G", - ALC888_ACER_ASPIRE_6530G), + ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G", ALC888_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G", -- cgit v1.2.2 From 95a618bdac29c7b0f1a516aec9fc37626dec1af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20R=C3=BCnkaru?= Date: Mon, 23 Nov 2009 22:23:49 +0200 Subject: ALSA: hda - Make Dell Vostro 1015n mic and speaker switching work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dell Vostro 1015n uses Conexant CX20583-10Z (0x14f1:5067). Patch is based on "olpc-xo-1_5" branch. Dell uses digital mic. Signed-off-by: Einar Rünkaru Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 134 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 0b097fa5421f..36dd5a6bf874 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2009,6 +2009,46 @@ static void cxt5066_automic(struct hda_codec *codec) } } +/* toggle input of built-in digital mic and mic jack appropriately */ +static void cxt5066_vostro_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int present; + + struct hda_verb ext_mic_present[] = { + /* enable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, + + /* switch to external mic input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0}, + + /* disable internal digital mic */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb ext_mic_absent[] = { + /* enable internal mic, port C */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* switch to internal mic input */ + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, + + /* disable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + + present = snd_hda_jack_detect(codec, 0x1a); + if (present) { + snd_printdd("CXT5066: external microphone detected\n"); + snd_hda_sequence_write(codec, ext_mic_present); + } else { + snd_printdd("CXT5066: external microphone absent\n"); + snd_hda_sequence_write(codec, ext_mic_absent); + } +} + /* mute internal speaker if HP is plugged */ static void cxt5066_hp_automute(struct hda_codec *codec) { @@ -2041,6 +2081,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) } } +/* unsolicited event for jack sensing */ +static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_vostro_automic(codec); + break; + } +} + static const struct hda_input_mux cxt5066_analog_mic_boost = { .num_items = 5, .items = { @@ -2282,6 +2336,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { { } /* end */ }; +static struct hda_verb cxt5066_init_verbs_vostro[] = { + /* Port A: headphones */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* Port B: external microphone */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port C: unused */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port D: unused */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port E: unused, but has primary EAPD */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + /* Port F: unused */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port G: internal speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* DAC2: unused */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* Digital microphone port */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* Audio input selectors */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + + /* Disable SPDIF */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* enable unsolicited events for Port A and B */ + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + static struct hda_verb cxt5066_init_verbs_portd_lo[] = { {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, { } /* end */ @@ -2303,6 +2418,7 @@ enum { CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ + CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ CXT5066_MODELS }; @@ -2310,6 +2426,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = { [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", + [CXT5066_DELL_VOSTO] = "dell-vostro" }; static struct snd_pci_quirk cxt5066_cfg_tbl[] = { @@ -2318,6 +2435,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x02f5, "Dell", CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), {} }; @@ -2382,6 +2500,19 @@ static int patch_cxt5066(struct hda_codec *codec) /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ + spec->input_mux = NULL; + break; + case CXT5066_DELL_VOSTO: + codec->patch_ops.unsol_event = cxt5066_vostro_event; + spec->init_verbs[0] = cxt5066_init_verbs_vostro; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->port_d_mode = 0; + + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ spec->input_mux = NULL; break; @@ -2402,6 +2533,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5051 }, { .id = 0x14f15066, .name = "CX20582 (Pebble)", .patch = patch_cxt5066 }, + { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", + .patch = patch_cxt5066 }, {} /* terminator */ }; @@ -2409,6 +2542,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045"); MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); MODULE_ALIAS("snd-hda-codec-id:14f15066"); +MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -- cgit v1.2.2 From bbb3c644bd9967753ce8c214c5e64b27c361d2a4 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Tue, 24 Nov 2009 22:51:05 -0500 Subject: ALSA: intel8x0: Mute External Amplifier by default for Gateway 4525GZ BugLink: https://bugs.launchpad.net/bugs/487884 This Gateway model needs External Amplifier muted for audible playback, so set the inv_eapd quirk for it. Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/intel8x0.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index aac20fb4aad2..b990143636f1 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2062,6 +2062,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { .name = "MSI P4 ATX 645 Ultra", .type = AC97_TUNE_HP_ONLY }, + { + .subvendor = 0x161f, + .subdevice = 0x203a, + .name = "Gateway 4525GZ", /* AD1981B */ + .type = AC97_TUNE_INV_EAPD + }, { .subvendor = 0x1734, .subdevice = 0x0088, -- cgit v1.2.2 From 0b587fc4d35afb1bc0fc3d890084bb14c78372dc Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 25 Nov 2009 18:27:20 -0500 Subject: ALSA: hda: Fix max PCM level to 0 dB for Fujitsu-Siemens laptops using CX20549 (Venice) BugLink: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4792 Cristian reported that these models have really bad sound above 6 dB and proposed the original patch. I've updated the comment to reflect this change. Signed-off-by: Daniel T Chen Reported-by: Cristian Klein Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 36dd5a6bf874..60810ba899d1 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1171,9 +1171,10 @@ static int patch_cxt5045(struct hda_codec *codec) switch (codec->subsystem_id >> 16) { case 0x103c: - /* HP laptop has a really bad sound over 0dB on NID 0x17. - * Fix max PCM level to 0 dB - * (originall it has 0x2b steps with 0dB offset 0x14) + case 0x1734: + /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB + * on NID 0x17. Fix max PCM level to 0 dB + * (originally it has 0x2b steps with 0dB offset 0x14) */ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, (0x14 << AC_AMPCAP_OFFSET_SHIFT) | -- cgit v1.2.2 From bfc9902599549736b9c6445e1e2235b8542f64a6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 27 Nov 2009 12:22:44 +0100 Subject: ALSA: hda - Don't trigger pin-sense for STAC/IDT codecs STAC/IDT codecs seem to behave weird when SET_PIN_SENSE verb is issued before reading the jack-detection although the TRIG_REQ pin capability is given by the hardware. Since snd_hda_jack_detect() issues the SET_PIN_SENSE verb simply judging from the pincap, we have to revert the change in the commit d56757abc11a21996d9839c0d4e3b2c3666cd318 ALSA: hda - Replace the rest of jack-detections with snd_hda_jack_detect() to plain GET_PIN_SENSE verb without triggering. Reported-by: Jiri Slaby Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 2a45375d79f8..6b0bc040c3b1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4440,7 +4440,14 @@ static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; - return snd_hda_jack_detect(codec, nid); + /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT + * codecs behave wrongly when SET_PIN_SENSE is triggered, although + * the pincap gives TRIG_REQ bit. + */ + if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE) + return 1; + return 0; } static void stac92xx_line_out_detect(struct hda_codec *codec, -- cgit v1.2.2 From 45d4ebf1a6255f2234a041685789cbecac3453f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Nov 2009 11:58:30 +0100 Subject: ALSA: hda - Add a position_fix quirk for MSI Wind U115 MSI Wind U115 seems to require position_fix=1 explicitly. Otherwise it screws up PulseAudio. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 91bcbdad5af5..238651bab3f5 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2234,6 +2234,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), {} }; -- cgit v1.2.2 From 854206b074581957e7b5c955001c329f94986b4c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 30 Nov 2009 18:22:04 +0100 Subject: ALSA: hda - Fix Cxt5047 test mode The NID 0x1a of Conexant 5047 chip is a mic boost volume only with the output amp unlike 5045 chip. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 60810ba899d1..a09c03c3f62b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1410,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = { .get = conexant_mux_enum_get, .put = conexant_mux_enum_put, }, - HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), - HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT), { } /* end */ }; -- cgit v1.2.2 From cfc9b06f0befe50ef02253f72b76946363549031 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Dec 2009 12:19:37 +0100 Subject: ALSA: hda - Add a pin-fix for FSC Amilo Pi1505 FSC Amilo Pi 1505 has a buggy BIOS and doesn't set up the HP and speaker pins properly. Add the pinfix entry for that. Reference: Novell bnc#557403 https://bugzilla.novell.com/show_bug.cgi?id=557403 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7e8b17a1769a..a38a81e53863 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14543,6 +14543,27 @@ static struct alc_config_preset alc861_presets[] = { }, }; +/* Pin config fixes */ +enum { + PINFIX_FSC_AMILO_PI1505, +}; + +static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = { + { 0x0b, 0x0221101f }, /* HP */ + { 0x0f, 0x90170310 }, /* speaker */ + { } +}; + +static const struct alc_fixup alc861_fixups[] = { + [PINFIX_FSC_AMILO_PI1505] = { + .pins = alc861_fsc_amilo_pi1505_pinfix + }, +}; + +static struct snd_pci_quirk alc861_fixup_tbl[] = { + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), + {} +}; static int patch_alc861(struct hda_codec *codec) { @@ -14566,6 +14587,8 @@ static int patch_alc861(struct hda_codec *codec) board_config = ALC861_AUTO; } + alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups); + if (board_config == ALC861_AUTO) { /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); -- cgit v1.2.2 From 2f703e7a2ea5f6d5ea14a7b2cd0d31be07839ac6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 1 Dec 2009 14:17:37 +0100 Subject: ALSA: hda - Add position_fix quirk for HP dv3 HP dv3 requires position_fix=1. Reference: Novell bnc#555935 https://bugzilla.novell.com/show_bug.cgi?id=555935 Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 238651bab3f5..d822bfc6cad6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2233,6 +2233,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_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), + SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), {} -- cgit v1.2.2 From 274693f37090ada2cadd09944ab883f05ea6ebe6 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 3 Dec 2009 10:07:50 +0100 Subject: ALSA: hda - Add ALC661/259, ALC892/888VD support Fixed List: 1. Add alc_read_coef_idx function 2. Add ALC661 ALC259 3. Add ALC892 ALC888VD Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 44 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a38a81e53863..98e117bac90a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1394,6 +1394,17 @@ static void alc_pick_fixup(struct hda_codec *codec, add_verb(codec->spec, fix->verbs); } +static int alc_read_coef_idx(struct hda_codec *codec, + unsigned int coef_idx) +{ + unsigned int val; + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, + coef_idx); + val = snd_hda_codec_read(codec, 0x20, 0, + AC_VERB_GET_PROC_COEF, 0); + return val; +} + /* * ALC888 */ @@ -3472,7 +3483,7 @@ static int alc_build_pcms(struct hda_codec *codec) snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), "%s Analog", codec->chip_name); info->name = spec->stream_name_analog; - + if (spec->stream_analog_playback) { if (snd_BUG_ON(!spec->multiout.dac_nids)) return -EINVAL; @@ -13445,6 +13456,13 @@ static int patch_alc269(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); + if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC259", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + } + board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, alc269_models, alc269_cfg_tbl); @@ -17444,6 +17462,13 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); + if (alc_read_coef_idx(codec, 0)==0x8020){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC661", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + } + board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, alc662_models, alc662_cfg_tbl); @@ -17510,6 +17535,20 @@ static int patch_alc662(struct hda_codec *codec) return 0; } +static int patch_alc888(struct hda_codec *codec) +{ + if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){ + kfree(codec->chip_name); + codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL); + if (!codec->chip_name) + return -ENOMEM; + patch_alc662(codec); + } else { + patch_alc882(codec); + } + return 0; +} + /* * patch entries */ @@ -17541,8 +17580,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 }, { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", .patch = patch_alc882 }, - { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 }, + { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 }, + { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 }, {} /* terminator */ }; -- cgit v1.2.2 From ac2c92e0cd06387ecee8115f5fa385fba6413c42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Dec 2009 10:14:10 +0100 Subject: ALSA: hda - Fix memory leaks in the previous patch The previous hack for replacing the codec name give memory leaks at error paths. This patch fixes them. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 98e117bac90a..d967836f36bb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13459,8 +13459,10 @@ static int patch_alc269(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC259", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; + } } board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, @@ -17465,8 +17467,10 @@ static int patch_alc662(struct hda_codec *codec) if (alc_read_coef_idx(codec, 0)==0x8020){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC661", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; + } } board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, @@ -17540,13 +17544,13 @@ static int patch_alc888(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){ kfree(codec->chip_name); codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL); - if (!codec->chip_name) + if (!codec->chip_name) { + alc_free(codec); return -ENOMEM; - patch_alc662(codec); - } else { - patch_alc882(codec); + } + return patch_alc662(codec); } - return 0; + return patch_alc882(codec); } /* -- cgit v1.2.2 From fb716c0b7bed36064cd41d800c8f339f41adf084 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Fri, 27 Nov 2009 18:18:33 +0100 Subject: snd-fm801: autodetect SF64-PCR (tuner-only) card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When primary AC97 is not found, don't fail with tons of AC97 errors. Assume that the card is SF64-PCR (tuner-only). This makes the SF64-PCR radio card work "out of the box". Also fixes a bug that can cause an oops here:         if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) { when tea575x_tuner == 16, it passes this check and causes problems a couple lines below:         chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1]; Tested with SF64-PCR, but I don't have any of those sound or sound+radio cards to test if I didn't break anything. Signed-off-by: Ondrej Zary Signed-off-by: Takashi Iwai --- sound/pci/fm801.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 60cdb9e0b68d..83508b3964fb 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -55,7 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card * * 1 = MediaForte 256-PCS * 2 = MediaForte 256-PCPR * 3 = MediaForte 64-PCR - * 16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card + * 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card * High 16-bits are video (radio) device number + 1 */ static int tea575x_tuner[SNDRV_CARDS]; @@ -67,7 +67,10 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); module_param_array(tea575x_tuner, int, NULL, 0444); -MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner."); +MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only)."); + +#define TUNER_ONLY (1<<4) +#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF) /* * Direct registers @@ -160,7 +163,7 @@ struct fm801 { unsigned int multichannel: 1, /* multichannel support */ secondary: 1; /* secondary codec */ unsigned char secondary_addr; /* address of the secondary codec */ - unsigned int tea575x_tuner; /* tuner flags */ + unsigned int tea575x_tuner; /* tuner access method & flags */ unsigned short ply_ctrl; /* playback control */ unsigned short cap_ctrl; /* capture control */ @@ -1287,7 +1290,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume) { unsigned short cmdw; - if (chip->tea575x_tuner & 0x0010) + if (chip->tea575x_tuner & TUNER_ONLY) goto __ac97_ok; /* codec cold reset + AC'97 warm reset */ @@ -1296,11 +1299,13 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume) udelay(100); outw(0, FM801_REG(chip, CODEC_CTRL)); - if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) { - snd_printk(KERN_ERR "Primary AC'97 codec not found\n"); - if (! resume) - return -EIO; - } + if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) + if (!resume) { + snd_printk(KERN_INFO "Primary AC'97 codec not found, " + "assume SF64-PCR (tuner-only)\n"); + chip->tea575x_tuner = 3 | TUNER_ONLY; + goto __ac97_ok; + } if (chip->multichannel) { if (chip->secondary_addr) { @@ -1414,7 +1419,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, return err; } chip->port = pci_resource_start(pci, 0); - if ((tea575x_tuner & 0x0010) == 0) { + if ((tea575x_tuner & TUNER_ONLY) == 0) { if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED, "FM801", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); @@ -1429,6 +1434,14 @@ static int __devinit snd_fm801_create(struct snd_card *card, chip->multichannel = 1; snd_fm801_chip_init(chip, 0); + /* init might set tuner access method */ + tea575x_tuner = chip->tea575x_tuner; + + if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) { + pci_clear_master(pci); + free_irq(chip->irq, chip); + chip->irq = -1; + } if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_fm801_free(chip); @@ -1438,12 +1451,13 @@ static int __devinit snd_fm801_create(struct snd_card *card, snd_card_set_dev(card, &pci->dev); #ifdef TEA575X_RADIO - if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) { + if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 && + (tea575x_tuner & TUNER_TYPE_MASK) < 4) { chip->tea.dev_nr = tea575x_tuner >> 16; chip->tea.card = card; chip->tea.freq_fixup = 10700; chip->tea.private_data = chip; - chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1]; + chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1]; snd_tea575x_init(&chip->tea); } #endif @@ -1483,7 +1497,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci, sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->port, chip->irq); - if (tea575x_tuner[dev] & 0x0010) + if (chip->tea575x_tuner & TUNER_ONLY) goto __fm801_tuner_only; if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) { -- cgit v1.2.2 From af901ca181d92aac3a7dc265144a9081a86d8f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Goddard=20Rosa?= Date: Sat, 14 Nov 2009 13:09:05 -0200 Subject: tree-wide: fix assorted typos all over the place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That is "success", "unknown", "through", "performance", "[re|un]mapping" , "access", "default", "reasonable", "[con]currently", "temperature" , "channel", "[un]used", "application", "example","hierarchy", "therefore" , "[over|under]flow", "contiguous", "threshold", "enough" and others. Signed-off-by: André Goddard Rosa Signed-off-by: Jiri Kosina --- sound/pci/ca0106/ca0106_proc.c | 2 +- sound/pci/cs46xx/imgs/cwcdma.asp | 9 +++++---- sound/pci/emu10k1/emu10k1x.c | 2 +- sound/pci/hda/patch_cmedia.c | 2 +- sound/pci/hda/patch_realtek.c | 2 +- sound/pci/rme9652/hdspm.c | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index c62b7d10ec61..8d13092300da 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -233,7 +233,7 @@ static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 val snd_iprintf(buffer, "user-defined\n"); break; default: - snd_iprintf(buffer, "unkown\n"); + snd_iprintf(buffer, "unknown\n"); break; } snd_iprintf(buffer, "Sample Bits: "); diff --git a/sound/pci/cs46xx/imgs/cwcdma.asp b/sound/pci/cs46xx/imgs/cwcdma.asp index 09d24c76f034..a65e1193c89a 100644 --- a/sound/pci/cs46xx/imgs/cwcdma.asp +++ b/sound/pci/cs46xx/imgs/cwcdma.asp @@ -26,10 +26,11 @@ // // // The purpose of this code is very simple: make it possible to tranfser -// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host) -// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters) -// task always alters the samples in some how, however it's from 48khz -> 48khz. The -// alterations are not audible, but AC3 wont work. +// the samples 'as they are' with no alteration from a PCMreader +// SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF. +// SRC (source rate converters) task always alters the samples in somehow, +// however it's from 48khz -> 48khz. +// The alterations are not audible, but AC3 wont work. // // ... // | diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 36e08bd2b3cc..360e3809a60b 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -184,7 +184,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard."); * The hardware has 3 channels for playback and 1 for capture. * - channel 0 is the front channel * - channel 1 is the rear channel - * - channel 2 is the center/lfe chanel + * - channel 2 is the center/lfe channel * Volume is controlled by the AC97 for the front and rear channels by * the PCM Playback Volume, Sigmatel Surround Playback Volume and * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 780e1a72114a..8917071d5b6a 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -66,7 +66,7 @@ struct cmi_spec { struct hda_pcm pcm_rec[2]; /* PCM information */ - /* pin deafault configuration */ + /* pin default configuration */ hda_nid_t pin_nid[NUM_PINS]; unsigned int def_conf[NUM_PINS]; unsigned int pin_def_confs; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ff20048504b6..872731eb49e8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6619,7 +6619,7 @@ static struct hda_input_mux alc889A_mb31_capture_source = { /* Front Mic (0x01) unused */ { "Line", 0x2 }, /* Line 2 (0x03) unused */ - /* CD (0x04) unsused? */ + /* CD (0x04) unused? */ }, }; diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 0dce331a2a3b..a1b10d1a384d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3017,7 +3017,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, insel = "Coaxial"; break; default: - insel = "Unkown"; + insel = "Unknown"; } switch (hdspm->control_register & HDSPM_SyncRefMask) { @@ -3028,7 +3028,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, syncref = "MADI"; break; default: - syncref = "Unkown"; + syncref = "Unknown"; } snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel, syncref); -- cgit v1.2.2 From 4b7e180335d23296170a5fa8c1f074722f94b253 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Mon, 7 Dec 2009 15:07:46 -0800 Subject: ALSA: hda - iMac 9,1 sound patch. This is an updated patch for the Apple iMac 9,1 model to add sound. Original patch posted here: http://article.gmane.org/gmane.linux.alsa.devel/61361/match= I have been using this patch for a while now and have to say it works vary well, except for a few minor things: With the iMac 24-inch 3.06GHz Intel Core 2 Duo everything seems to be working as it should, although I have not looked into the microphone (never really use one, nor have any apps to test, my guess is it doesn't work, or I never figured out how to get it to work). With the iMac 24-inch 2.66GHz Intel Core 2 Duo everything is the same as with the above machine except I'm hearing a light scratchy/distortion noise come out of the speakers when using headphones(above machine does not do this). Other than that the sound level is great(especially with good Dj headphones). Signed-off-by: Justin P. Mattock Tested-by: Justin P. Mattock Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 111 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d967836f36bb..d0d14ed7ce81 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -208,6 +208,7 @@ enum { ALC885_MBP3, ALC885_MB5, ALC885_IMAC24, + ALC885_IMAC91, ALC883_3ST_2ch_DIG, ALC883_3ST_6ch_DIG, ALC883_3ST_6ch, @@ -7050,6 +7051,20 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc885_imac91_mixer[] = { + HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + 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, 0x00, HDA_INPUT), + HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), + { } /* end */ +}; + + static struct snd_kcontrol_new alc882_w2jc_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -7505,6 +7520,66 @@ static struct hda_verb alc885_mbp3_init_verbs[] = { { } }; +/* iMac 9,1 */ +static struct hda_verb alc885_imac91_init_verbs[] = { + /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* Internal Speakers: output 0 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: use output 1 when in LineOut mode */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; + /* iMac 24 mixer. */ static struct snd_kcontrol_new alc885_imac24_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), @@ -7551,6 +7626,26 @@ static void alc885_mbp3_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; } +static void alc885_imac91_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + +} + +static void alc885_imac91_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Headphone insertion or removal. */ + if ((res >> 26) == ALC880_HP_EVENT) + alc885_imac91_automute(codec); +} static struct hda_verb alc882_targa_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -8718,6 +8813,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC885_MB5] = "mb5", [ALC885_MBP3] = "mbp3", [ALC885_IMAC24] = "imac24", + [ALC885_IMAC91] = "imac91", [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig", [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig", [ALC883_3ST_6ch] = "3stack-6ch", @@ -8891,6 +8987,7 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31), SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3), SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91), SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5), /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2, * so apparently no perfect solution yet @@ -9002,6 +9099,20 @@ static struct alc_config_preset alc882_presets[] = { .setup = alc885_imac24_setup, .init_hook = alc885_imac24_init_hook, }, + [ALC885_IMAC91] = { + .mixers = { alc885_imac91_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_imac91_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mbp_4ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes), + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc885_imac91_unsol_event, + .init_hook = alc885_imac91_automute, + }, [ALC882_TARGA] = { .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs, -- cgit v1.2.2 From 23033b2bce4361f2859ee0331f97c9056dae7091 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Dec 2009 12:36:52 +0100 Subject: ALSA: hda - Add missing Line-Out and PCM switches as slave Realtek codecs may have "PCM" and "Line-Out" playback switches, and they can be slaves for vmaster. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d0d14ed7ce81..0fbcbeef1418 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2401,6 +2401,8 @@ static const char *alc_slave_sws[] = { "Speaker Playback Switch", "Mono Playback Switch", "IEC958 Playback Switch", + "Line-Out Playback Switch", + "PCM Playback Switch", NULL, }; -- cgit v1.2.2 From d11f74c62fb4a1fefd39085570fb6dfa7b9ab2bb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Dec 2009 12:52:47 +0100 Subject: ALSA: hda - Exclude unusable ADCs for ALC88x On Realtek codecs, a digital mic pin is connected often only to a single ADC. But the parser tries to set up all ADCs no matter whether the digital mic is available, and results in non-selectable input source. This patch adds a check of input-source availability of each ADC, and excludes ones that don't support all input sources. Reference: Novell bnc#561235 http://bugzilla.novell.com/show_bug.cgi?id=561235 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0fbcbeef1418..2a96bc78964d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10021,10 +10021,12 @@ static int patch_alc882(struct hda_codec *codec) spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */ if (!spec->adc_nids && spec->input_mux) { - int i; + int i, j; spec->num_adc_nids = 0; for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) { + const struct hda_input_mux *imux = spec->input_mux; hda_nid_t cap; + hda_nid_t items[16]; hda_nid_t nid = alc882_adc_nids[i]; unsigned int wcap = get_wcaps(codec, nid); /* get type */ @@ -10035,6 +10037,15 @@ static int patch_alc882(struct hda_codec *codec) err = snd_hda_get_connections(codec, nid, &cap, 1); if (err < 0) continue; + err = snd_hda_get_connections(codec, cap, items, + ARRAY_SIZE(items)); + if (err < 0) + continue; + for (j = 0; j < imux->num_items; j++) + if (imux->items[j].index >= err) + break; + if (j < imux->num_items) + continue; spec->private_capsrc_nids[spec->num_adc_nids] = cap; spec->num_adc_nids++; } -- cgit v1.2.2 From ee6e365e30f7ee89bd214ff1215aaf90e93d4c40 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 Dec 2009 17:23:33 +0100 Subject: ALSA: hda - Generalize EAPD inversion check in patch_analog.c Add a flag to spec field so that the EAPD inversion can be checked outside the relevant control callbacks. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 455a0494f907..447eda1f6770 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -72,7 +72,8 @@ struct ad198x_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; unsigned int jack_present :1; - unsigned int inv_jack_detect:1; + unsigned int inv_jack_detect:1; /* inverted jack-detection */ + unsigned int inv_eapd:1; /* inverted EAPD implementation */ #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; @@ -458,7 +459,7 @@ static struct hda_codec_ops ad198x_patch_ops = { /* * EAPD control - * the private value = nid | (invert << 8) + * the private value = nid */ #define ad198x_eapd_info snd_ctl_boolean_mono_info @@ -467,8 +468,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; - if (invert) + if (spec->inv_eapd) ucontrol->value.integer.value[0] = ! spec->cur_eapd; else ucontrol->value.integer.value[0] = spec->cur_eapd; @@ -480,11 +480,10 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; eapd = !!ucontrol->value.integer.value[0]; - if (invert) + if (spec->inv_eapd) eapd = !eapd; if (eapd == spec->cur_eapd) return 0; @@ -705,7 +704,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, - .private_value = 0x1b | (1 << 8), /* port-D, inversed */ + .private_value = 0x1b, /* port-D */ }, { } /* end */ }; @@ -1074,6 +1073,7 @@ static int patch_ad1986a(struct hda_codec *codec) spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; + spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ codec->patch_ops = ad198x_patch_ops; @@ -2124,7 +2124,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = { .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, - .private_value = 0x12 | (1 << 8), /* port-D, inversed */ + .private_value = 0x12, /* port-D */ }, { } /* end */ @@ -3065,6 +3065,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->input_mux = &ad1988_laptop_capture_source; spec->num_mixers = 1; spec->mixers[0] = ad1988_laptop_mixers; + spec->inv_eapd = 1; /* inverted EAPD */ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1988_laptop_init_verbs; if (board_config == AD1988_LAPTOP_DIG) -- cgit v1.2.2 From 396087eaead95fcb29eb36f1e59517aeb58c545e Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Dec 2009 10:44:47 +0100 Subject: ALSA: hda - Terradici HDA controllers does not support 64-bit mode Confirmed from vendor and tests in RedHat bugzilla #536782 . Signed-off-by: Jaroslav Kysela Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d822bfc6cad6..efcc4f7c57f2 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2450,6 +2450,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } } + /* disable 64bit DMA address for Teradici */ + /* it does not work with device 6549:1200 subsys e4a2:040b */ + if (chip->driver_type == AZX_DRIVER_TERA) + gcap &= ~ICH6_GCAP_64OK; + /* allow 64bit DMA address if supported by H/W */ if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64))) pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64)); -- cgit v1.2.2 From 7aee67466536bbf8bb44a95712c848a61c5a0acd Mon Sep 17 00:00:00 2001 From: David Santinoli Date: Wed, 9 Dec 2009 12:34:26 +0100 Subject: ALSA: hda/realtek: quirk for D945GCLF2 mainboard Quirk for the ALC662 found on the Intel D945GCLF2 (and possibly other) mainboards. Signed-off-by: David Santinoli Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2a96bc78964d..deecdd2d5d37 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16970,6 +16970,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", ALC663_ASUS_H13), + SND_PCI_QUIRK(0x8086, 0xd604, "Intel mobo", ALC662_3ST_2ch_DIG), {} }; -- cgit v1.2.2 From 482e46d4b7c9bfbb2edc047fafa85cee1b0fc1e1 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 9 Dec 2009 12:43:44 +0100 Subject: ALSA: ice1724 - aureon - modify WM8770 Master & DAC volume The volume levels in original implementation are incorrect and does not match the dB scale. The real range is linear (in the sense of the dB scale) from 0dB to -100dB. Remove logaritmic table and make all volumes from range 0dB..100dB. The tests are in RedHat's bugzilla #540817. Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/ice1712/aureon.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 110d16e52733..765d7bd4c3d4 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -689,32 +689,14 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e return change; } -static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -10000, 100, 1); static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1); static const DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0); -/* - * Logarithmic volume values for WM8770 - * Computed as 20 * Log10(255 / x) - */ -static const unsigned char wm_vol[256] = { - 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, - 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, - 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, - 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 -}; - -#define WM_VOL_MAX (sizeof(wm_vol) - 1) +#define WM_VOL_MAX 100 +#define WM_VOL_CNT 101 /* 0dB .. -100dB */ #define WM_VOL_MUTE 0x8000 static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master) @@ -724,7 +706,8 @@ static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned sho if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) nvol = 0; else - nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX]; + nvol = ((vol % WM_VOL_CNT) * (master % WM_VOL_CNT)) / + WM_VOL_MAX; wm_put(ice, index, nvol); wm_put_nocache(ice, index, 0x180 | nvol); @@ -820,7 +803,7 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = voices; uinfo->value.integer.min = 0; /* mute (-101dB) */ - uinfo->value.integer.max = 0x7F; /* 0dB */ + uinfo->value.integer.max = WM_VOL_MAX; /* 0dB */ return 0; } @@ -850,7 +833,7 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { unsigned int vol = ucontrol->value.integer.value[i]; - if (vol > 0x7f) + if (vol > WM_VOL_MAX) continue; vol |= spec->vol[ofs+i]; if (vol != spec->vol[ofs+i]) { -- cgit v1.2.2 From c357aab02ee8de1f833579861ebd1e5683d2e806 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Fri, 11 Dec 2009 07:51:54 +0100 Subject: ALSA: hda - Fix LED GPIO setup for HP laptops with IDT codecs This patch fixes an error in processing of the HP BIOS configuration to enable GPIO based mute LED indicator control. That error causes driver to enable such control on all HP systems with the 92HD75 IDT codecs and results in unnecessary toggling of the GPIO on mute control manipulation. It also adds support of the future HP BIOS configuration extension for the named control. New configuration string has a format HP_Mute_LED_P_G where P can be 0 or 1 and defines mute LED GPIO control state (low/high) that corresponds to the NOT muted state of the master volume and G is the index of the GPIO to use (0..9) Lastly, it adds more systems to the support of the audio implementation as found on HP B-series systems Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 95 ++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 27 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 6b0bc040c3b1..e66672317e57 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -209,6 +209,7 @@ struct sigmatel_spec { unsigned int gpio_data; unsigned int gpio_mute; unsigned int gpio_led; + unsigned int gpio_led_polarity; /* stream */ unsigned int stream_delay; @@ -4724,13 +4725,61 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) } } -static int hp_bseries_system(u32 subsystem_id) +/* + * This method searches for the mute LED GPIO configuration + * provided as OEM string in SMBIOS. The format of that string + * is HP_Mute_LED_P_G or HP_Mute_LED_P + * where P can be 0 or 1 and defines mute LED GPIO control state (low/high) + * that corresponds to the NOT muted state of the master volume + * and G is the index of the GPIO to use as the mute LED control (0..9) + * If _G portion is missing it is assigned based on the codec ID + * + * So, HP B-series like systems may have HP_Mute_LED_0 (current models) + * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings + */ +static int find_mute_led_gpio(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + const struct dmi_device *dev = NULL; + + if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, + NULL, dev))) { + if (sscanf(dev->name, "HP_Mute_LED_%d_%d", + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { + spec->gpio_led = 1 << spec->gpio_led; + return 1; + } + if (sscanf(dev->name, "HP_Mute_LED_%d", + &spec->gpio_led_polarity) == 1) { + switch (codec->vendor_id) { + case 0x111d7608: + /* GPIO 0 */ + spec->gpio_led = 0x01; + return 1; + case 0x111d7600: + case 0x111d7601: + case 0x111d7602: + case 0x111d7603: + /* GPIO 3 */ + spec->gpio_led = 0x08; + return 1; + } + } + } + } + return 0; +} + +static int hp_blike_system(u32 subsystem_id) { switch (subsystem_id) { - case 0x103c307e: - case 0x103c307f: - case 0x103c3080: - case 0x103c3081: + case 0x103c1520: + case 0x103c1521: + case 0x103c1523: + case 0x103c1524: + case 0x103c1525: case 0x103c1722: case 0x103c1723: case 0x103c1724: @@ -4739,6 +4788,14 @@ static int hp_bseries_system(u32 subsystem_id) case 0x103c1727: case 0x103c1728: case 0x103c1729: + case 0x103c172a: + case 0x103c172b: + case 0x103c307e: + case 0x103c307f: + case 0x103c3080: + case 0x103c3081: + case 0x103c7007: + case 0x103c7008: return 1; } return 0; @@ -4833,7 +4890,7 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, else spec->gpio_data |= spec->gpio_led; /* white */ - if (hp_bseries_system(codec->subsystem_id)) { + if (!spec->gpio_led_polarity) { /* LED state is inverted on these systems */ spec->gpio_data ^= spec->gpio_led; } @@ -5526,7 +5583,7 @@ again: break; } - if (hp_bseries_system(codec->subsystem_id)) { + if (hp_blike_system(codec->subsystem_id)) { pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || @@ -5544,26 +5601,10 @@ again: } } - if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { - const struct dmi_device *dev = NULL; - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, - NULL, dev))) { - if (strcmp(dev->name, "HP_Mute_LED_1")) { - switch (codec->vendor_id) { - case 0x111d7608: - spec->gpio_led = 0x01; - break; - case 0x111d7600: - case 0x111d7601: - case 0x111d7602: - case 0x111d7603: - spec->gpio_led = 0x08; - break; - } - break; - } - } - } + if (find_mute_led_gpio(codec)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { -- cgit v1.2.2 From b923528ed29dc2d12832f76b1b9e05848d9de853 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 11 Dec 2009 12:28:33 +0800 Subject: ALSA: hda - show HBR(High Bit Rate) pin cap in procfs Note that the HBR capability only applies to HDMI pin. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_proc.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2d627613aea3..f9a084a1378e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -258,6 +258,7 @@ enum { #define AC_PINCAP_VREF (0x37<<8) #define AC_PINCAP_VREF_SHIFT 8 #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ +#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */ /* Vref status (used in pin cap) */ #define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ #define AC_PINCAP_VREF_50 (1<<1) /* 50% */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 09476fc1ab64..8d381c891001 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -240,8 +240,11 @@ static void print_pin_caps(struct snd_info_buffer *buffer, /* Realtek uses this bit as a different meaning */ if ((codec->vendor_id >> 16) == 0x10ec) snd_iprintf(buffer, " R/L"); - else + else { + if (caps & AC_PINCAP_HBR) + snd_iprintf(buffer, " HBR"); snd_iprintf(buffer, " HDMI"); + } } if (caps & AC_PINCAP_TRIG_REQ) snd_iprintf(buffer, " Trigger"); -- cgit v1.2.2 From 728765b30a052317b6cb6111d4c4e66aba5c0099 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 11 Dec 2009 12:28:34 +0800 Subject: ALSA: intelhdmi - accept DisplayPort pin HDA036 spec states: DP (Display Port) indicates whether the Pin Complex Widget supports connection to a Display Port sink. Supported if set to 1. Note that it is possible for the pin widget to support more than one digital display connection type, e.g. HDMI and DP bit are both set to 1. Also export the DP pin cap in procfs. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_proc.c | 2 ++ sound/pci/hda/patch_intelhdmi.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f9a084a1378e..9000d52fccca 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -255,6 +255,9 @@ enum { * in HD-audio specification */ #define AC_PINCAP_HDMI (1<<7) /* HDMI pin */ +#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can + * coexist with AC_PINCAP_HDMI + */ #define AC_PINCAP_VREF (0x37<<8) #define AC_PINCAP_VREF_SHIFT 8 #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 8d381c891001..c9afc04adac8 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -246,6 +246,8 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " HDMI"); } } + if (caps & AC_PINCAP_DP) + snd_iprintf(buffer, " DP"); if (caps & AC_PINCAP_TRIG_REQ) snd_iprintf(buffer, " Trigger"); if (caps & AC_PINCAP_IMP_SENSE) diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 928df59be5d8..742f15eb3331 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -344,7 +344,7 @@ static int intel_hdmi_parse_codec(struct hda_codec *codec) break; case AC_WID_PIN: caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - if (!(caps & AC_PINCAP_HDMI)) + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) continue; if (intel_hdmi_add_pin(codec, nid) < 0) return -EINVAL; -- cgit v1.2.2 From 1ffc69a6e86aa9458046d1719957e091c8e95f7a Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 11 Dec 2009 12:28:35 +0800 Subject: ALSA: intelhdmi - channel mapping applies to Pin HDA036-A specifies that the Audio Sample Packet (ASP) Channel Mapping verbs apply to Digital Display Pin Complex instead of Converter. With this fix, channel mapping is working as expected for IbexPeak. Thanks to Marcin for pointing this out! Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 742f15eb3331..0d5dd1ba8205 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -436,14 +436,15 @@ static void hdmi_set_channel_count(struct hda_codec *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } -static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid) +static void hdmi_debug_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE int i; int slot; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, nid, 0, + slot = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_CHAN_SLOT, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", slot >> 4, slot & 0xf); @@ -619,7 +620,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, return ai->CA; } -static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, +static void hdmi_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, struct hdmi_audio_infoframe *ai) { int i; @@ -633,11 +635,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid, */ for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_CHAN_SLOT, (i << 4) | i); - hdmi_debug_channel_mapping(codec, nid); + hdmi_debug_channel_mapping(codec, pin_nid); } static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, @@ -676,7 +678,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, }; hdmi_setup_channel_allocation(codec, nid, &ai); - hdmi_setup_channel_mapping(codec, nid, &ai); for (i = 0; i < spec->num_pins; i++) { if (spec->pin_cvt[i] != nid) @@ -686,6 +687,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, pin_nid = spec->pin[i]; if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + hdmi_setup_channel_mapping(codec, pin_nid, &ai); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, &ai); hdmi_start_infoframe_trans(codec, pin_nid); -- cgit v1.2.2 From b14224bb74e19072c34617c501bceab94ebf579f Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 11 Dec 2009 12:28:36 +0800 Subject: ALSA: intelhdmi - add channel mapping for typical configurations IbexPeak is the first Intel HDMI audio codec to support channel mapping. Currently the outstanding problem is, the HDMI channel order do not agree with that of ALSA. This patch presents workaround for some typical use cases. It gives priority to the typical ALSA surround configurations, and defines channel mapping for them. We may need better kernel+userspace interactive channel mapping scheme. For example, in current scheme if user plays with the surround50 device, the kernel is unaware of this and will still select the surround41 channel allocation and channel mapping.. Thanks to Marcin for offering good tips! Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 89 +++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 22 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 0d5dd1ba8205..3990182777ee 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -145,6 +145,42 @@ struct cea_channel_speaker_allocation { int spk_mask; }; +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + /* * This is an ordered list! * @@ -152,32 +188,36 @@ struct cea_channel_speaker_allocation { * hdmi_setup_channel_allocation(). */ static struct cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 8 7 6 5 4 3 2 1 */ +/* channel: 7 6 5 4 3 2 1 0 */ { .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, /* 2.1 */ { .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, /* Dolby Surround */ { .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + { .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, { .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, { .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, { .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, { .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* 5.1 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, { .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, { .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, { .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, { .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, { .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, { .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, - /* 7.1 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, { .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, { .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, { .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, @@ -210,7 +250,6 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { { .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, }; - /* * HDA/HDMI auto parsing */ @@ -625,19 +664,25 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, struct hdmi_audio_infoframe *ai) { int i; + int ca = ai->CA; + int err; - if (!ai->CA) - return; - - /* - * TODO: adjust channel mapping if necessary - * ALSA sequence is front/surr/clfe/side? - */ + if (hdmi_channel_mapping[ca][1] == 0) { + for (i = 0; i < channel_allocations[ca].channels; i++) + hdmi_channel_mapping[ca][i] = i | (i << 4); + for (; i < 8; i++) + hdmi_channel_mapping[ca][i] = 0xf | (i << 4); + } - for (i = 0; i < 8; i++) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (i << 4) | i); + for (i = 0; i < 8; i++) { + err = snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + hdmi_channel_mapping[ca][i]); + if (err) { + snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); + break; + } + } hdmi_debug_channel_mapping(codec, pin_nid); } -- cgit v1.2.2 From 0287d970652027d5e299e0215578f228660a0e4e Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 11 Dec 2009 20:15:11 +0800 Subject: intelhdmi - dont power off HDA link For codecs without EPSS support (G45/IbexPeak), the hotplug event will be lost if the HDA is powered off during the time. After that the pin presence detection verb returns inaccurate info. So always power-on HDA link for !EPSS codecs. KarL offers the fact and Takashi recommends to flag hda_bus. Thanks! Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_intel.c | 3 ++- sound/pci/hda/patch_intelhdmi.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9000d52fccca..1d541b7f5547 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -639,6 +639,7 @@ struct hda_bus { unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ + unsigned int power_keep_link_on:1; /* don't power off HDA link */ }; /* diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index efcc4f7c57f2..e54420e691ae 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2082,7 +2082,8 @@ static void azx_power_notify(struct hda_bus *bus) } if (power_on) azx_init_chip(chip); - else if (chip->running && power_save_controller) + else if (chip->running && power_save_controller && + !bus->power_keep_link_on) azx_stop_chip(chip); } #endif /* CONFIG_SND_HDA_POWER_SAVE */ diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 3990182777ee..918f40378d52 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -391,6 +391,17 @@ static int intel_hdmi_parse_codec(struct hda_codec *codec) } } + /* + * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event + * can be lost and presence sense verb will become inaccurate if the + * HDA link is powered off at hot plug or hw initialization time. + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & + AC_PWRST_EPSS)) + codec->bus->power_keep_link_on = 1; +#endif + return 0; } -- cgit v1.2.2 From 52dc438606d1ef78b96f56cc04dbea9242005730 Mon Sep 17 00:00:00 2001 From: Alexey Fisher Date: Sat, 12 Dec 2009 11:16:41 +0200 Subject: ALSA: hda - Overwrite pin config on intel DG45ID board. The pin config provided by BIOS have some problems: 0x0221401f: [Jack] HP Out at Ext Front <-- other association and sequence 0x02a19020: [Jack] Mic at Ext Front <-- other association 0x01113014: [Jack] Speaker at Ext Rear <-- line out (not speaker) 0x01114010: [Jack] Speaker at Ext Rear <-- line out 0x01a19030: [Jack] Mic at Ext Rear <-- other association 0x01111012: [Jack] Speaker at Ext Rear <-- line out 0x01116011: [Jack] Speaker at Ext Rear <-- line out 0x40f000f0: [N/A] Other at Ext N/A 0x40f000f0: [N/A] Other at Ext N/A 0x40f000f0: [N/A] Other at Ext N/A 0x40f000f0: [N/A] Other at Ext N/A 0x40f000f0: [N/A] Other at Ext N/A 0x01451140: [Jack] SPDIF Out at Ext Rear 0x40f000f0: [N/A] Other at Ext N/A just overwrite it. Signed-off-by: Alexey Fisher Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index e66672317e57..3d59f8325848 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1539,6 +1539,13 @@ static unsigned int alienware_m17x_pin_configs[13] = { 0x904601b0, }; +static unsigned int intel_dg45id_pin_configs[14] = { + 0x02214230, 0x02A19240, 0x01013214, 0x01014210, + 0x01A19250, 0x01011212, 0x01016211, 0x40f000f0, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x014510A0, + 0x074510B0, 0x40f000f0 +}; + static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, @@ -1546,6 +1553,7 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, [STAC_DELL_EQ] = dell_m6_pin_configs, [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, + [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs, }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { -- cgit v1.2.2 From 950200e2ff11daae1c5d9426703bdd494603f38b Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 13 Dec 2009 14:11:02 -0500 Subject: ALSA: hda: Use ALC260_WILL quirk for another Acer model (0x1025007f) BugLink: https://bugs.launchpad.net/bugs/418627 The original reporter states that this quirk is necessary to obtain reasonable gain for playback. Without it, sound is inaudible. Tested with playback (spkr and hp) and capture. Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index deecdd2d5d37..c9e860709747 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6248,6 +6248,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = { static struct snd_pci_quirk alc260_cfg_tbl[] = { SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER), SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100), SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013), -- cgit v1.2.2 From 01f5966d2f36f08eb6604665eade69c9f38ffaed Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 13 Dec 2009 16:22:58 -0500 Subject: ALSA: hda: Fix max PCM level to 0 dB for AD1981_HP BugLink: https://bugs.launchpad.net/bugs/461062 The original reporter states that PCM maxes at +12 dB and results in very bad distortion. Cap PCM at 0 dB to resolve this symptom. Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 447eda1f6770..1a36137e13ec 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1789,6 +1789,14 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops.init = ad1981_hp_init; codec->patch_ops.unsol_event = ad1981_hp_unsol_event; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1981_THINKPAD: spec->mixers[0] = ad1981_thinkpad_mixers; -- cgit v1.2.2 From 6dd7dc767e35cfbb38f8c63a50b1c27acad25920 Mon Sep 17 00:00:00 2001 From: Stefan Ringel Date: Mon, 14 Dec 2009 11:27:11 +0100 Subject: ALSA: hda - Add PCI IDs for Nvidia G2xx-series Signed-off-by: Stefan Ringel Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e54420e691ae..9b56f937913e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2713,6 +2713,9 @@ static struct pci_device_id azx_ids[] = { { 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, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA }, { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA }, -- cgit v1.2.2 From f74890277a196949e4004fe2955e1d4fb3930f98 Mon Sep 17 00:00:00 2001 From: Steve Soule Date: Mon, 14 Dec 2009 11:06:03 -0700 Subject: ALSA: ac97_codec - increase timeout for analog sections to 5 second I have a Soundblaster 16PCI. For many years, alsa has had a bug where not all of the card's controls are detected (many alsa versions, many kernel versions). In particular, Master Playback Volume is usually not detected, and so I get no sound or extremely faint sound. The problem has always been inconsistent: sometimes all of the controls are detected correctly, and sometimes a partial set is detected. It works correctly about 10% of the time. Finally, I got around to tracking down the problem. When the driver fails, it prints the kernel message "AC'97 0 analog subsections not ready". This message is generated from the function snd_ac97_mixer() in ac97_codec.c. The message indicates that the card failed to come back after reset within the time limit. The time limit is 120 milliseconds. I tried increasing the time limit to 1 second, and found that this made the driver work about 70% of the time. I tried increasing it to 5 seconds, and it now seems to work 100% of the time. I expect that this change would be completely harmless for existing cards that work, and would only introduce additional delay for cards that do not work. ALSA bug#4032. Signed-off-by: Steve Soule Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 20cb60afb200..c11920623009 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -2122,7 +2122,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, } /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0); - end_time = jiffies + msecs_to_jiffies(120); + end_time = jiffies + msecs_to_jiffies(5000); do { if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) goto __ready_ok; -- cgit v1.2.2 From 5b0cb1d850c26893b1468b3a519433a1b7a176be Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 8 Dec 2009 16:13:32 +0100 Subject: ALSA: hda - add more NID->Control mapping This set of changes add missing NID values to some static control elemenents. Also, it handles all "Capture Source" or "Input Source" controls. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 64 +++++++++- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_generic.c | 3 +- sound/pci/hda/hda_local.h | 5 + sound/pci/hda/hda_proc.c | 23 ++-- sound/pci/hda/patch_analog.c | 31 +++++ sound/pci/hda/patch_cirrus.c | 4 + sound/pci/hda/patch_cmedia.c | 12 +- sound/pci/hda/patch_realtek.c | 120 ++++++++++++++++++- sound/pci/hda/patch_si3054.c | 1 + sound/pci/hda/patch_via.c | 273 +++++++++++++++++++++++++----------------- 11 files changed, 415 insertions(+), 122 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9cfdb771928c..20100b1548e1 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -931,6 +931,7 @@ static void snd_hda_codec_free(struct hda_codec *codec) #endif list_del(&codec->list); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -985,7 +986,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr mutex_init(&codec->control_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60); + snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); + snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); if (codec->bus->modelname) { @@ -1706,7 +1708,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); /** - * snd_hda_ctl-add - Add a control element and assign to the codec + * snd_hda_ctl_add - Add a control element and assign to the codec * @codec: HD-audio codec * @nid: corresponding NID (optional) * @kctl: the control element to assign @@ -1746,6 +1748,35 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); +/** + * snd_hda_add_nid - Assign a NID to a control element + * @codec: HD-audio codec + * @nid: corresponding NID (optional) + * @kctl: the control element to assign + * @index: index to kctl + * + * Add the given control element to an array inside the codec instance. + * This function is used when #snd_hda_ctl_add cannot be used for 1:1 + * NID:KCTL mapping - for example "Capture Source" selector. + */ +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid) +{ + struct hda_nid_item *item; + + if (nid > 0) { + item = snd_array_new(&codec->nids); + if (!item) + return -ENOMEM; + item->kctl = kctl; + item->index = index; + item->nid = nid; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL_HDA(snd_hda_add_nid); + /** * snd_hda_ctls_clear - Clear all controls assigned to the given codec * @codec: HD-audio codec @@ -1757,6 +1788,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->bus->card, items[i].kctl); snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); } /* pseudo device locking @@ -3476,6 +3508,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) for (; knew->name; knew++) { struct snd_kcontrol *kctl; + if (knew->iface == -1) /* skip this codec private value */ + continue; kctl = snd_ctl_new1(knew, codec); if (!kctl) return -ENOMEM; @@ -3496,6 +3530,32 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) } EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); +/** + * snd_hda_add_nids - assign nids to controls from the array + * @codec: the HDA codec + * @kctl: struct snd_kcontrol + * @index: index to kctl + * @nids: the array of hda_nid_t + * @size: count of hda_nid_t items + * + * This helper function assigns NIDs in the given array to a control element. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t *nids, unsigned int size) +{ + int err; + + for ( ; size > 0; size--, nids++) { + err = snd_hda_add_nid(codec, kctl, index, *nids); + if (err < 0) + return err; + } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_add_nids); + #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1d541b7f5547..0d08ad5bd898 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -789,6 +789,7 @@ struct hda_codec { u32 *wcaps; struct snd_array mixers; /* list of assigned mixer elements */ + struct snd_array nids; /* list of mapped mixer elements */ struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 092c6a7c2ff3..5ea21285ee1f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -861,7 +861,8 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec)); + err = snd_hda_ctl_add(codec, spec->adc_node->nid, + snd_ctl_new1(&cap_sel, codec)); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5778ae882b83..98cf3f4f3755 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -342,6 +342,8 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, const struct snd_pci_quirk *tbl); int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew); +int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t *nids, unsigned int size); /* * unsolicited event handler @@ -466,11 +468,14 @@ int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); struct hda_nid_item { struct snd_kcontrol *kctl; + unsigned int index; hda_nid_t nid; }; int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl); +int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, + unsigned int index, hda_nid_t nid); void snd_hda_ctls_clear(struct hda_codec *codec); /* diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index c9afc04adac8..2e27d6a8b446 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -61,18 +61,21 @@ static const char *get_wid_type_name(unsigned int wid_value) return "UNKNOWN Widget"; } -static void print_nid_mixers(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) +static void print_nid_array(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + struct snd_array *array) { int i; - struct hda_nid_item *items = codec->mixers.list; + struct hda_nid_item *items = array->list, *item; struct snd_kcontrol *kctl; - for (i = 0; i < codec->mixers.used; i++) { - if (items[i].nid == nid) { - kctl = items[i].kctl; + for (i = 0; i < array->used; i++) { + item = &items[i]; + if (item->nid == nid) { + kctl = item->kctl; snd_iprintf(buffer, " Control: name=\"%s\", index=%i, device=%i\n", - kctl->id.name, kctl->id.index, kctl->id.device); + kctl->id.name, kctl->id.index + item->index, + kctl->id.device); } } } @@ -528,7 +531,8 @@ static void print_gpio(struct snd_info_buffer *buffer, (data & (1<mixers); + print_nid_array(buffer, codec, nid, &codec->nids); } static void print_codec_info(struct snd_info_entry *entry, @@ -608,7 +612,8 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, " CP"); snd_iprintf(buffer, "\n"); - print_nid_mixers(buffer, codec, nid); + print_nid_array(buffer, codec, nid, &codec->mixers); + print_nid_array(buffer, codec, nid, &codec->nids); print_nid_pcms(buffer, codec, nid); /* volume knob is a special widget that always have connection diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 447eda1f6770..d418842373fd 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -174,6 +174,7 @@ static struct snd_kcontrol_new ad_beep_mixer[] = { static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + struct snd_kcontrol *kctl; unsigned int i; int err; @@ -239,6 +240,28 @@ static int ad198x_build_controls(struct hda_codec *codec) } ad198x_free_kctls(codec); /* no longer needed */ + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + if (!kctl) + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids, + spec->input_mux->num_items); + if (err < 0) + return err; + } + + /* assign IEC958 enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, + SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source"); + if (kctl) { + err = snd_hda_add_nid(codec, kctl, 0, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + } + return 0; } @@ -701,6 +724,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "External Amplifier", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, @@ -808,6 +832,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1a, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1986a_hp_master_sw_put, @@ -1608,6 +1633,7 @@ static struct snd_kcontrol_new ad1981_hp_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_NID_FLAG | 0x05, .name = "Master Playback Switch", .info = ad198x_eapd_info, .get = ad198x_eapd_get, @@ -2121,6 +2147,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "External Amplifier", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x12, .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, @@ -2242,6 +2269,7 @@ static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "IEC958 Playback Source", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, .info = ad1988_spdif_playback_source_info, .get = ad1988_spdif_playback_source_get, .put = ad1988_spdif_playback_source_put, @@ -3728,6 +3756,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -3756,6 +3785,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -4097,6 +4127,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, .name = "Master Playback Switch", .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 4b200da1bd18..d0b8c6dc7322 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -759,6 +759,10 @@ static int build_input(struct hda_codec *codec) err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; + err = snd_hda_add_nids(codec, kctl, 0, spec->adc_nid, + spec->num_inputs); + if (err < 0) + return err; } if (spec->num_inputs > 1 && !spec->mic_detect) { diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index a45c1169762b..cc1c22370a60 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -315,7 +315,8 @@ static struct hda_verb cmi9880_allout_init[] = { static int cmi9880_build_controls(struct hda_codec *codec) { struct cmi_spec *spec = codec->spec; - int err; + struct snd_kcontrol *kctl; + int i, err; err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer); if (err < 0) @@ -340,6 +341,15 @@ static int cmi9880_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nids(codec, kctl, i, spec->adc_nids, + spec->input_mux->num_items); + if (err < 0) + return err; + } return 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 888b6313eeca..6b0b8728f6b7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -627,6 +627,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, #define ALC_PIN_MODE(xname, nid, dir) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_pin_mode_info, \ .get = alc_pin_mode_get, \ .put = alc_pin_mode_put, \ @@ -678,6 +679,7 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, } #define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_gpio_data_info, \ .get = alc_gpio_data_get, \ .put = alc_gpio_data_put, \ @@ -732,6 +734,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, } #define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_spdif_ctrl_info, \ .get = alc_spdif_ctrl_get, \ .put = alc_spdif_ctrl_put, \ @@ -785,6 +788,7 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, #define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_eapd_ctrl_info, \ .get = alc_eapd_ctrl_get, \ .put = alc_eapd_ctrl_put, \ @@ -2410,6 +2414,15 @@ static const char *alc_slave_sws[] = { * build control elements */ +#define NID_MAPPING (-1) + +#define SUBDEV_SPEAKER_ (0 << 6) +#define SUBDEV_HP_ (1 << 6) +#define SUBDEV_LINE_ (2 << 6) +#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) +#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) +#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) + static void alc_free_kctls(struct hda_codec *codec); #ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -2424,8 +2437,11 @@ static struct snd_kcontrol_new alc_beep_mixer[] = { static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int err; - int i; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new *knew; + int i, j, err; + unsigned int u; + hda_nid_t nid; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -2494,6 +2510,73 @@ static int alc_build_controls(struct hda_codec *codec) } alc_free_kctls(codec); /* no longer needed */ + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); + if (!kctl) + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids, + spec->input_mux->num_items); + if (err < 0) + return err; + } + if (spec->cap_mixer) { + const char *kname = kctl ? kctl->id.name : NULL; + for (knew = spec->cap_mixer; knew->name; knew++) { + if (kname && strcmp(knew->name, kname) == 0) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nid(codec, kctl, i, + spec->adc_nids[i]); + if (err < 0) + return err; + } + } + } + + /* other nid->control mapping */ + for (i = 0; i < spec->num_mixers; i++) { + for (knew = spec->mixers[i]; knew->name; knew++) { + if (knew->iface != NID_MAPPING) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + if (kctl == NULL) + continue; + u = knew->subdevice; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0x3f; + if (nid == 0) + continue; + switch (u & 0xc0) { + case SUBDEV_SPEAKER_: + nid = spec->autocfg.speaker_pins[nid]; + break; + case SUBDEV_LINE_: + nid = spec->autocfg.line_out_pins[nid]; + break; + case SUBDEV_HP_: + nid = spec->autocfg.hp_pins[nid]; + break; + default: + continue; + } + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + u = knew->private_value; + for (j = 0; j < 4; j++, u >>= 8) { + nid = u & 0xff; + if (nid == 0) + continue; + err = snd_hda_add_nid(codec, kctl, 0, nid); + if (err < 0) + return err; + } + } + } return 0; } @@ -3781,6 +3864,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, #define PIN_CTL_TEST(xname,nid) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_test_pin_ctl_info, \ .get = alc_test_pin_ctl_get, \ .put = alc_test_pin_ctl_put, \ @@ -3790,6 +3874,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, #define PIN_SRC_TEST(xname,nid) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | nid, \ .info = alc_test_pin_src_info, \ .get = alc_test_pin_src_get, \ .put = alc_test_pin_src_put, \ @@ -5080,6 +5165,7 @@ static struct snd_kcontrol_new alc260_hp_output_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, .info = snd_ctl_boolean_mono_info, .get = alc260_hp_master_sw_get, .put = alc260_hp_master_sw_put, @@ -5118,6 +5204,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x11, .info = snd_ctl_boolean_mono_info, .get = alc260_hp_master_sw_get, .put = alc260_hp_master_sw_put, @@ -10188,8 +10275,14 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, .info = snd_ctl_boolean_mono_info, \ .get = alc262_hp_master_sw_get, \ .put = alc262_hp_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \ } + static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { ALC262_HP_MASTER_SWITCH, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), @@ -10347,6 +10440,12 @@ static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol, .info = snd_ctl_boolean_mono_info, \ .get = alc262_hippo_master_sw_get, \ .put = alc262_hippo_master_sw_put, \ + }, \ + { \ + .iface = NID_MAPPING, \ + .name = "Master Playback Switch", \ + .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \ + (SUBDEV_SPEAKER(0) << 16), \ } static struct snd_kcontrol_new alc262_hippo_mixer[] = { @@ -10820,11 +10919,17 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_fujitsu_master_sw_put, .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), }, + { + .iface = NID_MAPPING, + .name = "Master Playback Switch", + .private_value = 0x1b, + }, 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), @@ -10855,6 +10960,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_lenovo_3000_master_sw_put, @@ -11009,6 +11115,11 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { .get = alc_mux_enum_get, .put = alc262_ultra_mux_enum_put, }, + { + .iface = NID_MAPPING, + .name = "Capture Source", + .private_value = 0x15, + }, { } /* end */ }; @@ -12026,6 +12137,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12041,6 +12153,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12058,6 +12171,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13010,6 +13124,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13030,6 +13145,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 43b436c5d01b..f419ee8d75f0 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -122,6 +122,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol, #define SI3054_KCONTROL(kname,reg,mask) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = kname, \ + .subdevice = HDA_SUBDEV_NID_FLAG | reg, \ .info = si3054_switch_info, \ .get = si3054_switch_get, \ .put = si3054_switch_put, \ diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b70e26ad263f..64995e8e3a72 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,6 +54,8 @@ #include "hda_codec.h" #include "hda_local.h" +#define NID_MAPPING (-1) + /* amp values */ #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) @@ -157,6 +159,19 @@ struct via_spec { #endif }; +static struct via_spec * via_new_spec(struct hda_codec *codec) +{ + struct via_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return NULL; + + codec->spec = spec; + spec->codec = codec; + return spec; +} + static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { u32 vendor_id = codec->vendor_id; @@ -448,6 +463,22 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, return 0; } +static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, + struct snd_kcontrol_new *tmpl) +{ + struct snd_kcontrol_new *knew; + + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *tmpl; + knew->name = kstrdup(tmpl->name, GFP_KERNEL); + if (!knew->name) + return NULL; + return 0; +} + static void via_free_kctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -1088,24 +1119,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - hda_nid_t nid; + hda_nid_t nid = kcontrol->private_value; unsigned int pinsel; - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - break; - case VT2002P: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } /* use !! to translate conn sel 2 for VT1718S */ pinsel = !!snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, @@ -1127,29 +1143,24 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active) } } +static hda_nid_t side_mute_channel(struct via_spec *spec) +{ + switch (spec->codec_type) { + case VT1708: return 0x1b; + case VT1709_10CH: return 0x29; + case VT1708B_8CH: /* fall thru */ + case VT1708S: return 0x27; + default: return 0; + } +} + static int update_side_mute_status(struct hda_codec *codec) { /* mute side channel */ struct via_spec *spec = codec->spec; unsigned int parm = spec->hp_independent_mode ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; - hda_nid_t sw3; - - switch (spec->codec_type) { - case VT1708: - sw3 = 0x1b; - break; - case VT1709_10CH: - sw3 = 0x29; - break; - case VT1708B_8CH: - case VT1708S: - sw3 = 0x27; - break; - default: - sw3 = 0; - break; - } + hda_nid_t sw3 = side_mute_channel(spec); if (sw3) snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -1162,28 +1173,11 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid = spec->autocfg.hp_pins[0]; + hda_nid_t nid = kcontrol->private_value; unsigned int pinsel = ucontrol->value.enumerated.item[0]; /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; - - switch (spec->codec_type) { - case VT1718S: - nid = 0x34; - pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ - spec->multiout.num_dacs = 4; - break; - case VT2002P: - nid = 0x35; - break; - case VT1812: - nid = 0x3d; - break; - default: - nid = spec->autocfg.hp_pins[0]; - break; - } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid @@ -1207,18 +1201,55 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new via_hp_mixer[] = { +static struct snd_kcontrol_new via_hp_mixer[2] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Independent HP", - .count = 1, .info = via_independent_hp_info, .get = via_independent_hp_get, .put = via_independent_hp_put, }, - { } /* end */ + { + .iface = NID_MAPPING, + .name = "Independent HP", + }, }; +static int via_hp_build(struct via_spec *spec) +{ + struct snd_kcontrol_new *knew; + hda_nid_t nid; + + knew = via_clone_control(spec, &via_hp_mixer[0]); + if (knew == NULL) + return -ENOMEM; + + switch (spec->codec_type) { + case VT1718S: + nid = 0x34; + break; + case VT2002P: + nid = 0x35; + break; + case VT1812: + nid = 0x3d; + break; + default: + nid = spec->autocfg.hp_pins[0]; + break; + } + + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + knew->private_value = nid; + + knew = via_clone_control(spec, &via_hp_mixer[1]); + if (knew == NULL) + return -ENOMEM; + knew->subdevice = side_mute_channel(spec); + + return 0; +} + static void notify_aa_path_ctls(struct hda_codec *codec) { int i; @@ -1376,7 +1407,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new via_smart51_mixer[] = { +static struct snd_kcontrol_new via_smart51_mixer[2] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Smart 5.1", @@ -1385,9 +1416,36 @@ static struct snd_kcontrol_new via_smart51_mixer[] = { .get = via_smart51_get, .put = via_smart51_put, }, - {} /* end */ + { + .iface = NID_MAPPING, + .name = "Smart 5.1", + } }; +static int via_smart51_build(struct via_spec *spec) +{ + struct snd_kcontrol_new *knew; + int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; + hda_nid_t nid; + int i; + + knew = via_clone_control(spec, &via_smart51_mixer[0]); + if (knew == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(index); i++) { + nid = spec->autocfg.input_pins[index[i]]; + if (nid) { + knew = via_clone_control(spec, &via_smart51_mixer[1]); + if (knew == NULL) + return -ENOMEM; + knew->subdevice = nid; + } + } + + return 0; +} + /* capture mixer elements */ static struct snd_kcontrol_new vt1708_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -1819,8 +1877,9 @@ static struct hda_pcm_stream vt1708_pcm_digital_capture = { static int via_build_controls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err; - int i; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new *knew; + int err, i; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -1845,6 +1904,28 @@ static int via_build_controls(struct hda_codec *codec) return err; } + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids, + spec->input_mux->num_items); + if (err < 0) + return err; + } + + /* other nid->control mapping */ + for (i = 0; i < spec->num_mixers; i++) { + for (knew = spec->mixers[i]; knew->name; knew++) { + if (knew->iface != NID_MAPPING) + continue; + kctl = snd_hda_find_mixer_ctl(codec, knew->name); + if (kctl == NULL) + continue; + err = snd_hda_add_nid(codec, kctl, 0, + knew->subdevice); + } + } + /* init power states */ set_jack_power_state(codec); analog_low_current_mode(codec, 1); @@ -2481,9 +2562,9 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -2554,12 +2635,10 @@ static int patch_vt1708(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708_parse_auto_config(codec); if (err < 0) { @@ -2597,7 +2676,6 @@ static int patch_vt1708(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1708_loopbacks; #endif - spec->codec = codec; INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -3010,9 +3088,9 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -3032,12 +3110,10 @@ static int patch_vt1709_10ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -3126,12 +3202,10 @@ static int patch_vt1709_6ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - err = vt1709_parse_auto_config(codec); if (err < 0) { via_free(codec); @@ -3581,9 +3655,9 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -3605,12 +3679,10 @@ static int patch_vt1708B_8ch(struct hda_codec *codec) if (get_codec_type(codec) == VT1708BCE) return patch_vt1708S(codec); /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708B_parse_auto_config(codec); if (err < 0) { @@ -3657,12 +3729,10 @@ static int patch_vt1708B_4ch(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708B_parse_auto_config(codec); if (err < 0) { @@ -4071,9 +4141,9 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -4103,12 +4173,10 @@ static int patch_vt1708S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1708S_parse_auto_config(codec); if (err < 0) { @@ -4443,7 +4511,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -4464,12 +4532,10 @@ static int patch_vt1702(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1702_parse_auto_config(codec); if (err < 0) { @@ -4865,9 +4931,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -4888,12 +4954,10 @@ static int patch_vt1718S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1718S_parse_auto_config(codec); if (err < 0) { @@ -5014,6 +5078,7 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Mic Capture Switch", + .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, .count = 1, .info = vt1716s_dmic_info, .get = vt1716s_dmic_get, @@ -5361,9 +5426,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); - spec->mixers[spec->num_mixers++] = via_smart51_mixer; + via_smart51_build(spec); return 1; } @@ -5384,12 +5449,10 @@ static int patch_vt1716S(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1716S_parse_auto_config(codec); if (err < 0) { @@ -5719,7 +5782,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -5741,12 +5804,10 @@ static int patch_vt2002P(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt2002P_parse_auto_config(codec); if (err < 0) { @@ -6070,7 +6131,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - spec->mixers[spec->num_mixers++] = via_hp_mixer; + via_hp_build(spec); return 1; } @@ -6092,12 +6153,10 @@ static int patch_vt1812(struct hda_codec *codec) int err; /* create a codec specific record */ - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = via_new_spec(codec); if (spec == NULL) return -ENOMEM; - codec->spec = spec; - /* automatic parse from the BIOS config */ err = vt1812_parse_auto_config(codec); if (err < 0) { -- cgit v1.2.2 From 9e3fd8719f624a43575b56a4777b1552399a8be8 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 8 Dec 2009 17:45:25 +0100 Subject: ALSA: hda - introduce HDA_SUBDEV_AMP_FLAG (ControlAmp in proc) The purpose of this changeset is to show information about amplifier setting in the codec proc file. Something like: Control: name="Front Playback Volume", index=0, device=0 ControlAmp: chs=3, dir=Out, idx=0, ofs=0 Control: name="Front Playback Switch", index=0, device=0 ControlAmp: chs=3, dir=In, idx=2, ofs=0 Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 14 +++++++++----- sound/pci/hda/hda_local.h | 11 ++++++++--- sound/pci/hda/hda_proc.c | 8 ++++++++ sound/pci/hda/patch_analog.c | 12 +++++++----- sound/pci/hda/patch_cirrus.c | 2 ++ sound/pci/hda/patch_conexant.c | 1 + sound/pci/hda/patch_realtek.c | 18 ++++++++++-------- sound/pci/hda/patch_sigmatel.c | 3 ++- sound/pci/hda/patch_via.c | 4 +++- 9 files changed, 50 insertions(+), 23 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 20100b1548e1..c9af15ed7f10 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1723,19 +1723,22 @@ EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl); * * snd_hda_ctl_add() checks the control subdev id field whether * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower - * bits value is taken as the NID to assign. + * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit + * specifies if kctl->private_value is a HDA amplifier value. */ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol *kctl) { int err; + unsigned short flags = 0; struct hda_nid_item *item; - if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) { - if (nid == 0) - nid = kctl->id.subdevice & 0xffff; + if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) + flags |= HDA_NID_ITEM_AMP; + if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) + nid = kctl->id.subdevice & 0xffff; + if (kctl->id.subdevice & 0xf0000000) kctl->id.subdevice = 0; - } err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) return err; @@ -1744,6 +1747,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, return -ENOMEM; item->kctl = kctl; item->nid = nid; + item->flags = flags; return 0; } EXPORT_SYMBOL_HDA(snd_hda_ctl_add); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 98cf3f4f3755..0a256471f812 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -31,6 +31,7 @@ * in snd_hda_ctl_add(), so that this value won't appear in the outside. */ #define HDA_SUBDEV_NID_FLAG (1U << 31) +#define HDA_SUBDEV_AMP_FLAG (1U << 30) /* * for mixer controls @@ -42,7 +43,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -63,7 +64,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -81,7 +82,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ @@ -466,10 +467,14 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); +/* flags for hda_nid_item */ +#define HDA_NID_ITEM_AMP (1<<0) + struct hda_nid_item { struct snd_kcontrol *kctl; unsigned int index; hda_nid_t nid; + unsigned short flags; }; int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 2e27d6a8b446..f97d35de66c4 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -76,6 +76,14 @@ static void print_nid_array(struct snd_info_buffer *buffer, " Control: name=\"%s\", index=%i, device=%i\n", kctl->id.name, kctl->id.index + item->index, kctl->id.device); + if (item->flags & HDA_NID_ITEM_AMP) + snd_iprintf(buffer, + " ControlAmp: chs=%lu, dir=%s, " + "idx=%lu, ofs=%lu\n", + get_amp_channels(kctl), + get_amp_direction(kctl) ? "Out" : "In", + get_amp_index(kctl), + get_amp_offset(kctl)); } } } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d418842373fd..5e2bb181a149 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -832,7 +832,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1a, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x1a, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1986a_hp_master_sw_put, @@ -2602,7 +2602,9 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, if (! knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | + HDA_SUBDEV_AMP_FLAG | + get_amp_nid_(val); knew->private_value = val; return 0; } @@ -3756,7 +3758,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -3785,7 +3787,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -4127,7 +4129,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_NID_FLAG | 0x21, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, .name = "Master Playback Switch", .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index d0b8c6dc7322..e51f6658aa2c 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -500,6 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } @@ -513,6 +514,7 @@ static int add_volume(struct hda_codec *codec, const char *name, knew.private_value = pval; snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); } diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a09c03c3f62b..b68650af40a9 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2178,6 +2178,7 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x10, .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = snd_hda_mixer_amp_volume_put, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6b0b8728f6b7..87bf7bd6292a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4414,7 +4414,9 @@ static int add_control(struct alc_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | + HDA_SUBDEV_AMP_FLAG | + get_amp_nid_(val); knew->private_value = val; return 0; } @@ -10919,7 +10921,7 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_fujitsu_master_sw_put, @@ -10960,7 +10962,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x1b, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_lenovo_3000_master_sw_put, @@ -12137,7 +12139,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12153,7 +12155,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12171,7 +12173,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13124,7 +13126,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13145,7 +13147,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x14, + .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3d59f8325848..1ee586b65b63 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2702,7 +2702,8 @@ stac_control_new(struct sigmatel_spec *spec, return NULL; } if (nid) - knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + knew->subdevice = HDA_SUBDEV_NID_FLAG | + HDA_SUBDEV_AMP_FLAG | nid; return knew; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 64995e8e3a72..b94cdee5eb53 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -458,7 +458,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_NID_FLAG | + HDA_SUBDEV_AMP_FLAG | + get_amp_nid_(val); knew->private_value = val; return 0; } -- cgit v1.2.2 From 5e26dfd0615868872cb44842f1e1428c7b414ab0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 10 Dec 2009 13:57:01 +0100 Subject: ALSA: hda - simplify usage of HDA_SUBDEV_AMP_FLAG The HDA_SUBDEV_NID_FLAG is duplicate for amplifier control elements. Move get_amp_nid_() call to the snd_hda_ctl_add() function. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 7 +++++-- sound/pci/hda/hda_local.h | 6 +++--- sound/pci/hda/patch_analog.c | 16 ++++++---------- sound/pci/hda/patch_cirrus.c | 4 ++-- sound/pci/hda/patch_conexant.c | 2 +- sound/pci/hda/patch_realtek.c | 21 +++++++++------------ sound/pci/hda/patch_sigmatel.c | 8 +++----- sound/pci/hda/patch_via.c | 4 +--- 8 files changed, 30 insertions(+), 38 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c9af15ed7f10..c848ec0f085e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1733,11 +1733,14 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, unsigned short flags = 0; struct hda_nid_item *item; - if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) + if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) { flags |= HDA_NID_ITEM_AMP; + if (nid == 0) + nid = get_amp_nid_(kctl->private_value); + } if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0) nid = kctl->id.subdevice & 0xffff; - if (kctl->id.subdevice & 0xf0000000) + if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) kctl->id.subdevice = 0; err = snd_ctl_add(codec->bus->card, kctl); if (err < 0) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 0a256471f812..d505d052972e 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -43,7 +43,7 @@ /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ @@ -64,7 +64,7 @@ /* mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put, \ @@ -82,7 +82,7 @@ /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | (nid), \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ .info = snd_hda_mixer_amp_switch_info, \ .get = snd_hda_mixer_amp_switch_get, \ .put = snd_hda_mixer_amp_switch_put_beep, \ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 5e2bb181a149..e75b5e5a1d55 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -209,9 +209,7 @@ static int ad198x_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, - get_amp_nid_(spec->beep_amp), - kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } @@ -832,7 +830,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x1a, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1986a_hp_master_sw_put, @@ -2602,9 +2600,7 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, if (! knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | - HDA_SUBDEV_AMP_FLAG | - get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } @@ -3758,7 +3754,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -3787,7 +3783,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = ad1884a_mobile_master_sw_put, @@ -4129,7 +4125,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { /* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x21, + .subdevice = HDA_SUBDEV_AMP_FLAG, .name = "Master Playback Switch", .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index e51f6658aa2c..eeb91f6a06c2 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -501,7 +501,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index, snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); + return snd_hda_ctl_add(codec, 0, *kctlp); } static int add_volume(struct hda_codec *codec, const char *name, @@ -515,7 +515,7 @@ static int add_volume(struct hda_codec *codec, const char *name, snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); *kctlp = snd_ctl_new1(&knew, codec); (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; - return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp); + return snd_hda_ctl_add(codec, 0, *kctlp); } static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b68650af40a9..1ab2958a290b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2178,7 +2178,7 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x10, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_volume_info, .get = snd_hda_mixer_amp_volume_get, .put = snd_hda_mixer_amp_volume_put, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 87bf7bd6292a..cb7679551bdc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2482,8 +2482,7 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, - get_amp_nid_(spec->beep_amp), kctl); + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; } @@ -4414,9 +4413,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | - HDA_SUBDEV_AMP_FLAG | - get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } @@ -10921,7 +10918,7 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_fujitsu_master_sw_put, @@ -10962,7 +10959,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x1b, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc262_lenovo_3000_master_sw_put, @@ -12139,7 +12136,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12155,7 +12152,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -12173,7 +12170,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13126,7 +13123,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, @@ -13147,7 +13144,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_NID_FLAG | HDA_SUBDEV_AMP_FLAG | 0x14, + .subdevice = HDA_SUBDEV_AMP_FLAG, .info = snd_hda_mixer_amp_switch_info, .get = snd_hda_mixer_amp_switch_get, .put = alc268_acer_master_sw_put, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1ee586b65b63..0bafea9d5106 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2685,7 +2685,7 @@ static struct snd_kcontrol_new * stac_control_new(struct sigmatel_spec *spec, struct snd_kcontrol_new *ktemp, const char *name, - hda_nid_t nid) + unsigned int subdev) { struct snd_kcontrol_new *knew; @@ -2701,9 +2701,7 @@ stac_control_new(struct sigmatel_spec *spec, spec->kctls.alloced--; return NULL; } - if (nid) - knew->subdevice = HDA_SUBDEV_NID_FLAG | - HDA_SUBDEV_AMP_FLAG | nid; + knew->subdevice = subdev; return knew; } @@ -2713,7 +2711,7 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec, unsigned long val) { struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, - get_amp_nid_(val)); + HDA_SUBDEV_AMP_FLAG); if (!knew) return -ENOMEM; knew->index = idx; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b94cdee5eb53..de4839e46762 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -458,9 +458,7 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, if (!knew->name) return -ENOMEM; if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_NID_FLAG | - HDA_SUBDEV_AMP_FLAG | - get_amp_nid_(val); + knew->subdevice = HDA_SUBDEV_AMP_FLAG; knew->private_value = val; return 0; } -- cgit v1.2.2 From 3c55494670745e523f69b56edb66ca0b50a470c2 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Mon, 14 Dec 2009 18:00:36 -0800 Subject: ALSA: cs5535audio: free OLPC quirks from reliance on MGEODE_LX cpu optimization Previously, OLPC support for the mic extensions was only enabled in the ALSA driver if CONFIG_OLPC and CONFIG_MGEODE_LX were both set. This was because the old geode GPIO code was written in a manner that assumed CONFIG_MGEODE_LX. With the new cs553x-gpio driver, this is no longer the case; as such, we can drop the requirement on CONFIG_MGEODE_LX and instead include a requirement on GPIOLIB. We use the generic GPIO API rather than the cs553x-specific API. Signed-off-by: Andres Salomon Cc: Takashi Iwai Cc: Jordan Crouse Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- sound/pci/cs5535audio/Makefile | 2 -- sound/pci/cs5535audio/cs5535audio.c | 1 + sound/pci/cs5535audio/cs5535audio.h | 4 +++- sound/pci/cs5535audio/cs5535audio_olpc.c | 26 +++++++++++++++++++------- 4 files changed, 23 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile index fda7a94c992f..ccc642269b9e 100644 --- a/sound/pci/cs5535audio/Makefile +++ b/sound/pci/cs5535audio/Makefile @@ -4,9 +4,7 @@ snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o -ifdef CONFIG_MGEODE_LX snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o -endif # Toplevel Module Dependency obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 05f56e04849b..91e7faf69bbb 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -389,6 +389,7 @@ probefail_out: static void __devexit snd_cs5535audio_remove(struct pci_dev *pci) { + olpc_quirks_cleanup(); snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h index 7a298ac662e3..51966d782a3c 100644 --- a/sound/pci/cs5535audio/cs5535audio.h +++ b/sound/pci/cs5535audio/cs5535audio.h @@ -99,10 +99,11 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); int snd_cs5535audio_resume(struct pci_dev *pci); #endif -#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX) +#ifdef CONFIG_OLPC void __devinit olpc_prequirks(struct snd_card *card, struct snd_ac97_template *ac97); int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97); +void __devexit olpc_quirks_cleanup(void); void olpc_analog_input(struct snd_ac97 *ac97, int on); void olpc_mic_bias(struct snd_ac97 *ac97, int on); @@ -128,6 +129,7 @@ static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) { return 0; } +static inline void olpc_quirks_cleanup(void) { } static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { } static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { } static inline void olpc_capture_open(struct snd_ac97 *ac97) { } diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c index 5c6814335cd7..50da49be9ae5 100644 --- a/sound/pci/cs5535audio/cs5535audio_olpc.c +++ b/sound/pci/cs5535audio/cs5535audio_olpc.c @@ -13,10 +13,13 @@ #include #include #include +#include #include #include "cs5535audio.h" +#define DRV_NAME "cs5535audio-olpc" + /* * OLPC has an additional feature on top of the regular AD1888 codec features. * It has an Analog Input mode that is switched into (after disabling the @@ -38,10 +41,7 @@ void olpc_analog_input(struct snd_ac97 *ac97, int on) } /* set Analog Input through GPIO */ - if (on) - geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL); - else - geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL); + gpio_set_value(OLPC_GPIO_MIC_AC, on); } /* @@ -73,8 +73,7 @@ static int olpc_dc_info(struct snd_kcontrol *kctl, static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v) { - v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC, - GPIO_OUTPUT_VAL); + v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC); return 0; } @@ -153,6 +152,12 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) if (!machine_is_olpc()) return 0; + if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) { + printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n"); + return -EIO; + } + gpio_direction_output(OLPC_GPIO_MIC_AC, 0); + /* drop the original AD1888 HPF control */ memset(&elem, 0, sizeof(elem)); elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -169,11 +174,18 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) { err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i], ac97->private_data)); - if (err < 0) + if (err < 0) { + gpio_free(OLPC_GPIO_MIC_AC); return err; + } } /* turn off the mic by default */ olpc_mic_bias(ac97, 0); return 0; } + +void __devexit olpc_quirks_cleanup(void) +{ + gpio_free(OLPC_GPIO_MIC_AC); +} -- cgit v1.2.2 From e7d2860b690d4f3bed6824757c540579638e3d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Goddard=20Rosa?= Date: Mon, 14 Dec 2009 18:01:06 -0800 Subject: tree-wide: convert open calls to remove spaces to skip_spaces() lib function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes use of skip_spaces() defined in lib/string.c for removing leading spaces from strings all over the tree. It decreases lib.a code size by 47 bytes and reuses the function tree-wide: text data bss dec hex filename 64688 584 592 65864 10148 (TOTALS-BEFORE) 64641 584 592 65817 10119 (TOTALS-AFTER) Also, while at it, if we see (*str && isspace(*str)), we can be sure to remove the first condition (*str) as the second one (isspace(*str)) also evaluates to 0 whenever *str == 0, making it redundant. In other words, "a char equals zero is never a space". Julia Lawall tried the semantic patch (http://coccinelle.lip6.fr) below, and found occurrences of this pattern on 3 more files: drivers/leds/led-class.c drivers/leds/ledtrig-timer.c drivers/video/output.c @@ expression str; @@ ( // ignore skip_spaces cases while (*str && isspace(*str)) { \(str++;\|++str;\) } | - *str && isspace(*str) ) Signed-off-by: André Goddard Rosa Cc: Julia Lawall Cc: Martin Schwidefsky Cc: Jeff Dike Cc: Ingo Molnar Cc: Thomas Gleixner Cc: "H. Peter Anvin" Cc: Richard Purdie Cc: Neil Brown Cc: Kyle McMartin Cc: Henrique de Moraes Holschuh Cc: David Howells Cc: Cc: Samuel Ortiz Cc: Patrick McHardy Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- sound/pci/hda/hda_hwdep.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index d24328661c6a..40ccb419b6e9 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "hda_codec.h" @@ -428,8 +429,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf) char *key, *val; struct hda_hint *hint; - while (isspace(*buf)) - buf++; + buf = skip_spaces(buf); if (!*buf || *buf == '#' || *buf == '\n') return 0; if (*buf == '=') @@ -444,8 +444,7 @@ static int parse_hints(struct hda_codec *codec, const char *buf) return -EINVAL; } *val++ = 0; - while (isspace(*val)) - val++; + val = skip_spaces(val); remove_trail_spaces(key); remove_trail_spaces(val); hint = get_hint(codec, key); -- cgit v1.2.2 From ebb83eeb6469bedda83b4dc6f23ddf93eb32b347 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 17 Dec 2009 12:23:00 +0100 Subject: ALSA: hda - More ALC663 fixes and support of compatible chips 1. Add more ASUS NB model. 2. Fixed alc663_m51va_setup M51VA has Digital Mic that NID is 0x12. The record source index is 0x9 for ALC663. So, to modify the alc663_m51va_setup function to index 0x9 and add analog Mic aupport function alc663_mode1_setup. 3. Add ASUS mode7 and mode8 modules for ALC663 Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 306 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 282 insertions(+), 24 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c9e860709747..287bb6019df9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -131,8 +131,8 @@ enum { enum { ALC269_BASIC, ALC269_QUANTA_FL1, - ALC269_ASUS_EEEPC_P703, - ALC269_ASUS_EEEPC_P901, + ALC269_ASUS_AMIC, + ALC269_ASUS_DMIC, ALC269_FUJITSU, ALC269_LIFEBOOK, ALC269_AUTO, @@ -188,6 +188,8 @@ enum { ALC663_ASUS_MODE4, ALC663_ASUS_MODE5, ALC663_ASUS_MODE6, + ALC663_ASUS_MODE7, + ALC663_ASUS_MODE8, ALC272_DELL, ALC272_DELL_ZM1, ALC272_SAMSUNG_NC10, @@ -13232,10 +13234,12 @@ static struct hda_verb alc269_eeepc_amic_init_verbs[] = { /* toggle speaker-output according to the hp-jack state */ static void alc269_speaker_automute(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; + unsigned int nid = spec->autocfg.hp_pins[0]; unsigned int present; unsigned char bits; - present = snd_hda_jack_detect(codec, 0x15); + present = snd_hda_jack_detect(codec, nid); bits = present ? AMP_IN_MUTE(0) : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, AMP_IN_MUTE(0), bits); @@ -13460,8 +13464,8 @@ static void alc269_auto_init(struct hda_codec *codec) static const char *alc269_models[ALC269_MODEL_LAST] = { [ALC269_BASIC] = "basic", [ALC269_QUANTA_FL1] = "quanta", - [ALC269_ASUS_EEEPC_P703] = "eeepc-p703", - [ALC269_ASUS_EEEPC_P901] = "eeepc-p901", + [ALC269_ASUS_AMIC] = "asus-amic", + [ALC269_ASUS_DMIC] = "asus-dmic", [ALC269_FUJITSU] = "fujitsu", [ALC269_LIFEBOOK] = "lifebook", [ALC269_AUTO] = "auto", @@ -13470,18 +13474,41 @@ static const char *alc269_models[ALC269_MODEL_LAST] = { static struct snd_pci_quirk alc269_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_EEEPC_P703), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_EEEPC_P703), + ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80JT", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_DMIC), + SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_AMIC), + SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_AMIC), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", - ALC269_ASUS_EEEPC_P901), + ALC269_ASUS_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_ASUS_EEEPC_P901), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_EEEPC_P901), + ALC269_ASUS_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_ASUS_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_ASUS_DMIC), SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), {} @@ -13511,7 +13538,7 @@ static struct alc_config_preset alc269_presets[] = { .setup = alc269_quanta_fl1_setup, .init_hook = alc269_quanta_fl1_init_hook, }, - [ALC269_ASUS_EEEPC_P703] = { + [ALC269_ASUS_AMIC] = { .mixers = { alc269_eeepc_mixer }, .cap_mixer = alc269_epc_capture_mixer, .init_verbs = { alc269_init_verbs, @@ -13525,7 +13552,7 @@ static struct alc_config_preset alc269_presets[] = { .setup = alc269_eeepc_amic_setup, .init_hook = alc269_eeepc_inithook, }, - [ALC269_ASUS_EEEPC_P901] = { + [ALC269_ASUS_DMIC] = { .mixers = { alc269_eeepc_mixer }, .cap_mixer = alc269_epc_capture_mixer, .init_verbs = { alc269_init_verbs, @@ -16160,6 +16187,52 @@ static struct snd_kcontrol_new alc663_g50v_mixer[] = { { } /* end */ }; +static struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +static struct snd_kcontrol_new alc663_mode7_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("IntMic 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 snd_kcontrol_new alc663_mode8_mixer[] = { + HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch), + HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol), + HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch), + HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + + static struct snd_kcontrol_new alc662_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -16447,6 +16520,45 @@ static struct hda_verb alc272_dell_init_verbs[] = { {} }; +static struct hda_verb alc663_mode7_init_verbs[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + +static struct hda_verb alc663_mode8_init_verbs[] = { + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + static struct snd_kcontrol_new alc662_auto_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), @@ -16626,6 +16738,54 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) } } +static void alc663_two_hp_m7_speaker_automute(struct hda_codec *codec) +{ + unsigned int present1, present2; + + present1 = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + present2 = snd_hda_codec_read(codec, 0x21, 0, + AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + + if (present1 || present2) { + snd_hda_codec_write_cache(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } else { + snd_hda_codec_write_cache(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + } +} + +static void alc663_two_hp_m8_speaker_automute(struct hda_codec *codec) +{ + unsigned int present1, present2; + + present1 = snd_hda_codec_read(codec, 0x21, 0, + AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + present2 = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) + & AC_PINSENSE_PRESENCE; + + if (present1 || present2) { + snd_hda_codec_write_cache(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } else { + snd_hda_codec_write_cache(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + } +} + static void alc663_m51va_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -16645,7 +16805,7 @@ static void alc663_m51va_setup(struct hda_codec *codec) spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 1; + spec->int_mic.mux_idx = 9; spec->auto_mic = 1; } @@ -16657,7 +16817,17 @@ static void alc663_m51va_inithook(struct hda_codec *codec) /* ***************** Mode1 ******************************/ #define alc663_mode1_unsol_event alc663_m51va_unsol_event -#define alc663_mode1_setup alc663_m51va_setup + +static void alc663_mode1_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; + spec->auto_mic = 1; +} + #define alc663_mode1_inithook alc663_m51va_inithook /* ***************** Mode2 ******************************/ @@ -16674,7 +16844,7 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec, } } -#define alc662_mode2_setup alc663_m51va_setup +#define alc662_mode2_setup alc663_mode1_setup static void alc662_mode2_inithook(struct hda_codec *codec) { @@ -16695,7 +16865,7 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec, } } -#define alc663_mode3_setup alc663_m51va_setup +#define alc663_mode3_setup alc663_mode1_setup static void alc663_mode3_inithook(struct hda_codec *codec) { @@ -16716,7 +16886,7 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec, } } -#define alc663_mode4_setup alc663_m51va_setup +#define alc663_mode4_setup alc663_mode1_setup static void alc663_mode4_inithook(struct hda_codec *codec) { @@ -16737,7 +16907,7 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec, } } -#define alc663_mode5_setup alc663_m51va_setup +#define alc663_mode5_setup alc663_mode1_setup static void alc663_mode5_inithook(struct hda_codec *codec) { @@ -16758,7 +16928,7 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec, } } -#define alc663_mode6_setup alc663_m51va_setup +#define alc663_mode6_setup alc663_mode1_setup static void alc663_mode6_inithook(struct hda_codec *codec) { @@ -16766,6 +16936,50 @@ static void alc663_mode6_inithook(struct hda_codec *codec) alc_mic_automute(codec); } +/* ***************** Mode7 ******************************/ +static void alc663_mode7_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc663_two_hp_m7_speaker_automute(codec); + break; + case ALC880_MIC_EVENT: + alc_mic_automute(codec); + break; + } +} + +#define alc663_mode7_setup alc663_mode1_setup + +static void alc663_mode7_inithook(struct hda_codec *codec) +{ + alc663_two_hp_m7_speaker_automute(codec); + alc_mic_automute(codec); +} + +/* ***************** Mode8 ******************************/ +static void alc663_mode8_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc663_two_hp_m8_speaker_automute(codec); + break; + case ALC880_MIC_EVENT: + alc_mic_automute(codec); + break; + } +} + +#define alc663_mode8_setup alc663_m51va_setup + +static void alc663_mode8_inithook(struct hda_codec *codec) +{ + alc663_two_hp_m8_speaker_automute(codec); + alc_mic_automute(codec); +} + static void alc663_g71v_hp_automute(struct hda_codec *codec) { unsigned int present; @@ -16900,6 +17114,8 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { [ALC663_ASUS_MODE4] = "asus-mode4", [ALC663_ASUS_MODE5] = "asus-mode5", [ALC663_ASUS_MODE6] = "asus-mode6", + [ALC663_ASUS_MODE7] = "asus-mode7", + [ALC663_ASUS_MODE8] = "asus-mode8", [ALC272_DELL] = "dell", [ALC272_DELL_ZM1] = "dell-zm1", [ALC272_SAMSUNG_NC10] = "samsung-nc10", @@ -16916,12 +17132,22 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7), + SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8), + SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), + SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3), SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA), SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2), @@ -17205,6 +17431,36 @@ static struct alc_config_preset alc662_presets[] = { .setup = alc663_mode6_setup, .init_hook = alc663_mode6_inithook, }, + [ALC663_ASUS_MODE7] = { + .mixers = { alc663_mode7_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_mode7_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc663_mode7_unsol_event, + .setup = alc663_mode7_setup, + .init_hook = alc663_mode7_inithook, + }, + [ALC663_ASUS_MODE8] = { + .mixers = { alc663_mode8_mixer }, + .cap_mixer = alc662_auto_capture_mixer, + .init_verbs = { alc662_init_verbs, + alc663_mode8_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .hp_nid = 0x03, + .dac_nids = alc662_dac_nids, + .dig_out_nid = ALC662_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), + .channel_mode = alc662_3ST_2ch_modes, + .unsol_event = alc663_mode8_unsol_event, + .setup = alc663_mode8_setup, + .init_hook = alc663_mode8_inithook, + }, [ALC272_DELL] = { .mixers = { alc663_m51va_mixer }, .cap_mixer = alc272_auto_capture_mixer, @@ -17688,7 +17944,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, + { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 }, { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 }, + { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, -- cgit v1.2.2 From 254bba6a7e28c77d8b32d9eaeba02d4943ee844e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20R=C3=BCnkaru?= Date: Wed, 16 Dec 2009 22:16:13 +0200 Subject: ALSA: hda - Fixed internal mic initialization for Dell Vostro 1015 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed initialization of internal mic and added internal mic boost control Renamed analog mic boost control to ext mic boost contol. Name pair analog/digital seems too confusing for a normal user. Signed-off-by: Einar Rünkaru Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a09c03c3f62b..ca9ad9fddbf2 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -111,6 +111,7 @@ struct conexant_spec { unsigned int dell_automute; unsigned int port_d_mode; unsigned char ext_mic_bias; + unsigned int dell_vostro; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -2109,9 +2110,12 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int val; + hda_nid_t nid = kcontrol->private_value & 0xff; + int inout = (kcontrol->private_value & 0x100) ? + AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT; - val = snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT); + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, inout); ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; return 0; @@ -2123,6 +2127,9 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; unsigned int idx; + hda_nid_t nid = kcontrol->private_value & 0xff; + int inout = (kcontrol->private_value & 0x100) ? + AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT; if (!imux->num_items) return 0; @@ -2130,9 +2137,9 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, if (idx >= imux->num_items) idx = imux->num_items - 1; - snd_hda_codec_write_cache(codec, 0x17, 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | + AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout | imux->items[idx].index); return 1; @@ -2201,10 +2208,11 @@ static struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Analog Mic Boost Capture Enum", + .name = "Ext Mic Boost Capture Enum", .info = cxt5066_mic_boost_mux_enum_info, .get = cxt5066_mic_boost_mux_enum_get, .put = cxt5066_mic_boost_mux_enum_put, + .private_value = 0x17, }, HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), @@ -2212,6 +2220,18 @@ static struct snd_kcontrol_new cxt5066_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5066_vostro_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Int Mic Boost Capture Enum", + .info = cxt5066_mic_boost_mux_enum_info, + .get = cxt5066_mic_boost_mux_enum_get, + .put = cxt5066_mic_boost_mux_enum_put, + .private_value = 0x23 | 0x100, + }, + {} +}; + static struct hda_verb cxt5066_init_verbs[] = { {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ @@ -2397,11 +2417,16 @@ static struct hda_verb cxt5066_init_verbs_portd_lo[] = { /* initialize jack-sensing, too */ static int cxt5066_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; + snd_printdd("CXT5066: init\n"); conexant_init(codec); if (codec->patch_ops.unsol_event) { cxt5066_hp_automute(codec); - cxt5066_automic(codec); + if (spec->dell_vostro) + cxt5066_vostro_automic(codec); + else + cxt5066_automic(codec); } return 0; } @@ -2500,7 +2525,9 @@ static int patch_cxt5066(struct hda_codec *codec) spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; spec->port_d_mode = 0; + spec->dell_vostro = 1; /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; -- cgit v1.2.2 From c0f8faf0c7cd497ec7c1d61e1e9424f4384c1f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Einar=20R=C3=BCnkaru?= Date: Wed, 16 Dec 2009 22:41:36 +0200 Subject: ALSA: hda - Make use of beep device found in Dell Vostro 1015n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conexant CX20583-10Z has digital beep device with volume control. Making use of them. Signed-off-by: Einar Rünkaru Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ca9ad9fddbf2..c578c28f368e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -29,6 +29,7 @@ #include "hda_codec.h" #include "hda_local.h" +#include "hda_beep.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -477,6 +478,7 @@ static void conexant_free(struct hda_codec *codec) snd_array_free(&spec->jacks); } #endif + snd_hda_detach_beep_device(codec); kfree(codec->spec); } @@ -2229,6 +2231,7 @@ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = { .put = cxt5066_mic_boost_mux_enum_put, .private_value = 0x23 | 0x100, }, + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), {} }; @@ -2528,6 +2531,7 @@ static int patch_cxt5066(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; spec->port_d_mode = 0; spec->dell_vostro = 1; + snd_hda_attach_beep_device(codec, 0x13); /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; -- cgit v1.2.2 From 035eb0cff0671ada49ba9f3e5c9e7b0cb950efea Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Dec 2009 15:00:26 +0100 Subject: ALSA: hda - Fix missing capsrc_nids for ALC88x Some model quirks missed the corresponding capsrc_nids. This resulted in non-working capture source selection. Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 287bb6019df9..d9a9f0c7cf5b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9238,6 +9238,8 @@ static struct alc_config_preset alc882_presets[] = { .dac_nids = alc883_dac_nids, .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), .adc_nids = alc889_adc_nids, + .capsrc_nids = alc889_capsrc_nids, + .capsrc_nids = alc889_capsrc_nids, .dig_out_nid = ALC883_DIGOUT_NID, .dig_in_nid = ALC883_DIGIN_NID, .slave_dig_outs = alc883_slave_dig_outs, @@ -9284,6 +9286,7 @@ static struct alc_config_preset alc882_presets[] = { .dac_nids = alc883_dac_nids, .adc_nids = alc883_adc_nids_alt, .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, .dig_out_nid = ALC883_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, @@ -9430,6 +9433,7 @@ static struct alc_config_preset alc882_presets[] = { .dac_nids = alc883_dac_nids, .adc_nids = alc883_adc_nids_alt, .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, @@ -9491,6 +9495,7 @@ static struct alc_config_preset alc882_presets[] = { .dac_nids = alc883_dac_nids, .adc_nids = alc883_adc_nids_alt, .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), + .capsrc_nids = alc883_capsrc_nids, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_lenovo_101e_capture_source, @@ -9670,6 +9675,7 @@ static struct alc_config_preset alc882_presets[] = { alc880_gpio1_init_verbs }, .adc_nids = alc883_adc_nids, .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .capsrc_nids = alc883_capsrc_nids, .dac_nids = alc883_dac_nids, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .channel_mode = alc889A_mb31_6ch_modes, -- cgit v1.2.2 From d1409ae4cecb4af260759bdfdf88fafca23a9940 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Dec 2009 15:01:31 +0100 Subject: ALSA: hda - Fix NULL dereference in kctl-NID mapping in patch_realtek.c capsrc_nids can be NULL, and adc_nids should be taken as fallback. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 36556b10357a..012435212e58 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2517,7 +2517,10 @@ static int alc_build_controls(struct hda_codec *codec) if (!kctl) kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids, + hda_nid_t *nids = spec->capsrc_nids; + if (!nids) + nids = spec->adc_nids; + err = snd_hda_add_nids(codec, kctl, i, nids, spec->input_mux->num_items); if (err < 0) return err; -- cgit v1.2.2 From 2fef62c825f09e29d2f52dc187ddf6f99e28c7f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Dec 2009 08:48:42 +0100 Subject: ALSA: hda - Fix quirk for Maxdata obook4-1 Works fine with the auto-parser. Reference: Novell bnc#564940 https://bugzilla.novell.com/show_bug.cgi?id=564940 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d9a9f0c7cf5b..84bc2c7c4421 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8921,7 +8921,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ - SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO), SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG), -- cgit v1.2.2 From 0c2fd1bf4c6cb6095d8b3088d285167e66c12147 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Dec 2009 16:41:39 +0100 Subject: ALSA: hda - Check class to identify Nvidia controller chips Instead of listing all individual PCI IDs, check the matching with the PCI class together with the vendor id for Nvidia. This simplifies the pci id entries. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9b56f937913e..93eaf4fc39be 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2694,32 +2694,10 @@ static struct pci_device_id azx_ids[] = { /* 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, 0x0590), .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, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA }, - { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_NVIDIA }, /* Teradici */ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, /* Creative X-Fi (CA0110-IBG) */ -- cgit v1.2.2 From ef86f581f7e8b29cb58d7f4e892e1a91b3805124 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 19 Dec 2009 08:18:03 +0100 Subject: ALSA: Use kzalloc for allocating only one thing Use kzalloc rather than kcalloc(1,...) The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ @@ - kcalloc(1, + kzalloc( ...) // Signed-off-by: Julia Lawall Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index aeed4cc5aa79..20c1828e4bac 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12857,7 +12857,7 @@ static int patch_alc268(struct hda_codec *codec) int board_config; int i, has_beep, err; - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; -- cgit v1.2.2 From 440b004cf953bec2bc8cd91c64ae707fd7e25327 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 20 Dec 2009 12:04:08 +0100 Subject: ALSA: hda/realtek: Remove extra .capsrc_nids initialization for ALC889_INTEL Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_realtek.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8b375771b3ab..2d3f4f893ef3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9238,8 +9238,6 @@ static struct alc_config_preset alc882_presets[] = { .dac_nids = alc883_dac_nids, .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), .adc_nids = alc889_adc_nids, - .capsrc_nids = alc889_capsrc_nids, - .capsrc_nids = alc889_capsrc_nids, .dig_out_nid = ALC883_DIGOUT_NID, .dig_in_nid = ALC883_DIGIN_NID, .slave_dig_outs = alc883_slave_dig_outs, -- cgit v1.2.2 From e2595322a3a353a59cecd7f57e7aa421ecb02d12 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sat, 19 Dec 2009 18:19:02 -0500 Subject: ALSA: hda: Set Front Mic to input vref 50% for Lenovo 3000 Y410 BugLink: https://bugs.launchpad.net/bugs/479373 The OR has verified with hda-verb that the internal microphone needs VREF50 set for audible capture. Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 84bc2c7c4421..1554c3a6fd2e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10686,6 +10686,13 @@ static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = { {} }; +static struct hda_verb alc262_lenovo_3000_init_verbs[] = { + /* Front Mic pin: input vref at 50% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {} +}; + static struct hda_input_mux alc262_fujitsu_capture_source = { .num_items = 3, .items = { @@ -11728,7 +11735,8 @@ static struct alc_config_preset alc262_presets[] = { [ALC262_LENOVO_3000] = { .mixers = { alc262_lenovo_3000_mixer }, .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs, - alc262_lenovo_3000_unsol_verbs }, + alc262_lenovo_3000_unsol_verbs, + alc262_lenovo_3000_init_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, .hp_nid = 0x03, -- cgit v1.2.2 From 0f86a228f4a4639b3142ce0dad208433b2db377a Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 20 Dec 2009 22:51:18 +0100 Subject: ALSA: HDA: simplify Aspire 8930G verb array This patch just simplifies the 8930G verb array a bit. Just use the common ALC889 EAPD verb array to make things more consistent. The file is already huge enough already. Signed-off-by: Hector Martin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1554c3a6fd2e..cb97323acc17 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1665,9 +1665,6 @@ static struct hda_verb alc889_acer_aspire_8930g_verbs[] = { /* some bit here disables the other DACs. Init=0x4900 */ {0x20, AC_VERB_SET_COEF_INDEX, 0x08}, {0x20, AC_VERB_SET_PROC_COEF, 0x0000}, -/* Enable amplifiers */ - {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* DMIC fix * This laptop has a stereo digital microphone. The mics are only 1cm apart * which makes the stereo useless. However, either the mic or the ALC889 @@ -9386,7 +9383,8 @@ static struct alc_config_preset alc882_presets[] = { .mixers = { alc888_base_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, - alc889_acer_aspire_8930g_verbs }, + alc889_acer_aspire_8930g_verbs, + alc889_eapd_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, .num_adc_nids = ARRAY_SIZE(alc889_adc_nids), -- cgit v1.2.2 From 556eea9a926bff8f014b4f80522b4de97ae84213 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 20 Dec 2009 22:51:23 +0100 Subject: ALSA: HDA: remove useless mixers on Aspire 8930G This patch removes some extra mixers that do nothing on the Acer Aspire 8930G. The CD mixer is useless because the SATA DVD/Blu-Ray drive has no analog audio output, and the Side mixer is useless because we max out at 6ch anyway. Signed-off-by: Hector Martin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cb97323acc17..faeb74f28207 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1777,6 +1777,25 @@ static struct snd_kcontrol_new alc888_base_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc889_acer_aspire_8930g_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", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, + HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, 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), + { } /* end */ +}; + + static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -9380,7 +9399,7 @@ static struct alc_config_preset alc882_presets[] = { .init_hook = alc_automute_amp, }, [ALC888_ACER_ASPIRE_8930G] = { - .mixers = { alc888_base_mixer, + .mixers = { alc889_acer_aspire_8930g_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, alc889_acer_aspire_8930g_verbs, -- cgit v1.2.2 From f5de24b06aa46427500d0fdbe8616b73a71d8c28 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 20 Dec 2009 22:51:31 +0100 Subject: ALSA: HDA: add powersaving hook for Realtek The current Realtek code makes no specific provision for turning stuff off. The codec chip is placed into low-power mode generically, but this doesn't turn off any external hardware connected to it, in particular external amplifiers. This patch creates a hook function that is called by the codec suspend/resume functions. It ought to disable any external hardware in a device-specific way. I've implemented a generic ALC889 function that sets the EAPD pin properly, and used it for the Acer Aspire 8930G which can benefit from this feature. On my laptop, this results in ~0.5W extra savings. Signed-off-by: Hector Martin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index faeb74f28207..b3abe9ca826d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -337,6 +337,9 @@ struct alc_spec { /* hooks */ void (*init_hook)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); +#ifdef CONFIG_SND_HDA_POWER_SAVE + void (*power_hook)(struct hda_codec *codec, int power); +#endif /* for pin sensing */ unsigned int sense_updated: 1; @@ -388,6 +391,7 @@ struct alc_config_preset { void (*init_hook)(struct hda_codec *); #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_amp_list *loopbacks; + void (*power_hook)(struct hda_codec *codec, int power); #endif }; @@ -900,6 +904,7 @@ static void setup_preset(struct hda_codec *codec, spec->unsol_event = preset->unsol_event; spec->init_hook = preset->init_hook; #ifdef CONFIG_SND_HDA_POWER_SAVE + spec->power_hook = preset->power_hook; spec->loopback.amplist = preset->loopbacks; #endif @@ -1826,6 +1831,16 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[2] = 0x1b; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void alc889_power_eapd(struct hda_codec *codec, int power) +{ + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); +} +#endif + /* * ALC880 3-stack model * @@ -3619,12 +3634,29 @@ static void alc_free(struct hda_codec *codec) snd_hda_detach_beep_device(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct alc_spec *spec = codec->spec; + if (spec && spec->power_hook) + spec->power_hook(codec, 0); + return 0; +} +#endif + #ifdef SND_HDA_NEEDS_RESUME static int alc_resume(struct hda_codec *codec) { +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct alc_spec *spec = codec->spec; +#endif codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (spec && spec->power_hook) + spec->power_hook(codec, 1); +#endif return 0; } #endif @@ -3641,6 +3673,7 @@ static struct hda_codec_ops alc_patch_ops = { .resume = alc_resume, #endif #ifdef CONFIG_SND_HDA_POWER_SAVE + .suspend = alc_suspend, .check_power_status = alc_check_power_status, #endif }; @@ -9420,6 +9453,9 @@ static struct alc_config_preset alc882_presets[] = { .unsol_event = alc_automute_amp_unsol_event, .setup = alc889_acer_aspire_8930g_setup, .init_hook = alc_automute_amp, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .power_hook = alc889_power_eapd, +#endif }, [ALC888_ACER_ASPIRE_7730G] = { .mixers = { alc883_3ST_6ch_mixer, -- cgit v1.2.2 From d8d881dd2c814e1500558889d800cf78d11cf898 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Dec 2009 07:52:49 +0100 Subject: ALSA: hda - Fix NULL dereference with enable_beep=0 option Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 3d59f8325848..417fb22ae83c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -3779,15 +3779,16 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out err = snd_hda_attach_beep_device(codec, nid); if (err < 0) return err; - /* IDT/STAC codecs have linear beep tone parameter */ - codec->beep->linear_tone = 1; - /* if no beep switch is available, make its own one */ - caps = query_amp_caps(codec, nid, HDA_OUTPUT); - if (codec->beep && - !((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT)) { - err = stac92xx_beep_switch_ctl(codec); - if (err < 0) - return err; + if (codec->beep) { + /* IDT/STAC codecs have linear beep tone parameter */ + codec->beep->linear_tone = 1; + /* if no beep switch is available, make its own one */ + caps = query_amp_caps(codec, nid, HDA_OUTPUT); + if (!(caps & AC_AMPCAP_MUTE)) { + err = stac92xx_beep_switch_ctl(codec); + if (err < 0) + return err; + } } } #endif -- cgit v1.2.2 From 1a5ba2e9fc7999b8de2a71c7e7b9f58d752c05e4 Mon Sep 17 00:00:00 2001 From: Rafael Avila de Espindola Date: Tue, 22 Dec 2009 07:59:37 +0100 Subject: ALSA: hda - Add support for the new 27 inch IMacs With the attached patch I am able to use the sound on a new IMac 27. What works: *) Internal speakers *) Internal microphone *) Headphone I don't have an external mic or a SPDIF device to test the rest. Signed-off-by: Rafael Avila de Espindola Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 4b200da1bd18..fe0423c39598 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -66,6 +66,7 @@ struct cs_spec { /* available models */ enum { CS420X_MBP55, + CS420X_IMAC27, CS420X_AUTO, CS420X_MODELS }; @@ -827,7 +828,8 @@ static void cs_automute(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, hp_present ? 0 : PIN_OUT); } - if (spec->board_config == CS420X_MBP55) { + if (spec->board_config == CS420X_MBP55 || + spec->board_config == CS420X_IMAC27) { unsigned int gpio = hp_present ? 0x02 : 0x08; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); @@ -1069,12 +1071,14 @@ static int cs_parse_auto_config(struct hda_codec *codec) static const char *cs420x_models[CS420X_MODELS] = { [CS420X_MBP55] = "mbp55", + [CS420X_IMAC27] = "imac27", [CS420X_AUTO] = "auto", }; static struct snd_pci_quirk cs420x_cfg_tbl[] = { SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), + SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27), {} /* terminator */ }; @@ -1097,8 +1101,23 @@ static struct cs_pincfg mbp55_pincfgs[] = { {} /* terminator */ }; +static struct cs_pincfg imac27_pincfgs[] = { + { 0x09, 0x012b4050 }, + { 0x0a, 0x90100140 }, + { 0x0b, 0x90100142 }, + { 0x0c, 0x018b3020 }, + { 0x0d, 0x90a00110 }, + { 0x0e, 0x400000f0 }, + { 0x0f, 0x01cbe030 }, + { 0x10, 0x014be060 }, + { 0x12, 0x01ab9070 }, + { 0x15, 0x400000f0 }, + {} /* terminator */ +}; + static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { [CS420X_MBP55] = mbp55_pincfgs, + [CS420X_IMAC27] = imac27_pincfgs, }; static void fix_pincfg(struct hda_codec *codec, int model) @@ -1128,6 +1147,7 @@ static int patch_cs420x(struct hda_codec *codec) fix_pincfg(codec, spec->board_config); switch (spec->board_config) { + case CS420X_IMAC27: case CS420X_MBP55: /* GPIO1 = headphones */ /* GPIO3 = speakers */ -- cgit v1.2.2 From 9dc8398bab52931435fce403ce2eaf5822f28e58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Dec 2009 08:15:01 +0100 Subject: ALSA: hda - Add MSI blacklist A machine with AMD CPU with Nvidia board doesn't work with MSI. Reported-by: Robert J. King Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9b56f937913e..ff8ad46cc50e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2322,6 +2322,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) * white/black-list for enable_msi */ static struct snd_pci_quirk msi_black_list[] __devinitdata = { + SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ {} }; -- cgit v1.2.2 From 41116e926cb92292fa4fcbe888ae8133fa0038e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Dec 2009 09:00:14 +0100 Subject: ALSA: cs46xx - Fix suspend/resume with new DSP Fix the basic suspend/resume of snd-cs46xx drivers with new DSP. References: https://bugzilla.redhat.com/show_bug.cgi?id=498287 https://bugzilla.redhat.com/show_bug.cgi?id=160751 Tested-by: Florian Zumbiehl Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 2 +- sound/pci/cs46xx/dsp_spos.c | 42 +++++++++++++++++++++++++++++++++---- sound/pci/cs46xx/dsp_spos.h | 4 ++++ sound/pci/cs46xx/dsp_spos_scb_lib.c | 33 +++++++++++++---------------- 4 files changed, 58 insertions(+), 23 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 1be96ead4244..e6b4a879ae2e 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3597,7 +3597,7 @@ static struct cs_card_type __devinitdata cards[] = { #ifdef CONFIG_PM static unsigned int saved_regs[] = { BA0_ACOSV, - BA0_ASER_FADDR, + /*BA0_ASER_FADDR,*/ BA0_ASER_MASTER, BA1_PVOL, BA1_CVOL, diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index f4f0c8f5dad7..3e5ca8fb519f 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -298,6 +298,9 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip) if (ins->scbs[i].deleted) continue; cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) ); +#ifdef CONFIG_PM + kfree(ins->scbs[i].data); +#endif } kfree(ins->code.data); @@ -974,13 +977,11 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam index = find_free_scb_index (ins); + memset(&ins->scbs[index], 0, sizeof(ins->scbs[index])); strcpy(ins->scbs[index].scb_name, name); ins->scbs[index].address = dest; ins->scbs[index].index = index; - ins->scbs[index].proc_info = NULL; ins->scbs[index].ref_count = 1; - ins->scbs[index].deleted = 0; - spin_lock_init(&ins->scbs[index].lock); desc = (ins->scbs + index); ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER); @@ -1022,17 +1023,29 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) return desc; } +#define SCB_BYTES (0x10 * 4) + struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest) { struct dsp_scb_descriptor * desc; +#ifdef CONFIG_PM + /* copy the data for resume */ + scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL); + if (!scb_data) + return NULL; +#endif + desc = _map_scb (chip,name,dest); if (desc) { desc->data = scb_data; _dsp_create_scb(chip,scb_data,dest); } else { snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n"); +#ifdef CONFIG_PM + kfree(scb_data); +#endif } return desc; @@ -1988,7 +2001,28 @@ int cs46xx_dsp_resume(struct snd_cs46xx * chip) continue; _dsp_create_scb(chip, s->data, s->address); } - + for (i = 0; i < ins->nscb; i++) { + struct dsp_scb_descriptor *s = &ins->scbs[i]; + if (s->deleted) + continue; + if (s->updated) + cs46xx_dsp_spos_update_scb(chip, s); + if (s->volume_set) + cs46xx_dsp_scb_set_volume(chip, s, + s->volume[0], s->volume[1]); + } + if (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) { + cs46xx_dsp_enable_spdif_hw(chip); + snd_cs46xx_poke(chip, (ins->ref_snoop_scb->address + 2) << 2, + (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10); + if (ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) + cs46xx_poke_via_dsp(chip, SP_SPDOUT_CSUV, + ins->spdif_csuv_stream); + } + if (chip->dsp_spos_instance->spdif_status_in) { + cs46xx_poke_via_dsp(chip, SP_ASER_COUNTDOWN, 0x80000005); + cs46xx_poke_via_dsp(chip, SP_SPDIN_CONTROL, 0x800003ff); + } return 0; } #endif diff --git a/sound/pci/cs46xx/dsp_spos.h b/sound/pci/cs46xx/dsp_spos.h index f9e169d33c03..ca47a8114c7f 100644 --- a/sound/pci/cs46xx/dsp_spos.h +++ b/sound/pci/cs46xx/dsp_spos.h @@ -212,6 +212,7 @@ static inline void cs46xx_dsp_spos_update_scb (struct snd_cs46xx * chip, (scb->address + SCBsubListPtr) << 2, (scb->sub_list_ptr->address << 0x10) | (scb->next_scb_ptr->address)); + scb->updated = 1; } static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip, @@ -222,6 +223,9 @@ static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip, snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val); snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val); + scb->volume_set = 1; + scb->volume[0] = left; + scb->volume[1] = right; } #endif /* __DSP_SPOS_H__ */ #endif /* CONFIG_SND_CS46XX_NEW_DSP */ diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index dd7c41b037b4..00b148a10239 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -115,7 +115,6 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry, static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; - unsigned long flags; if ( scb->parent_scb_ptr ) { /* unlink parent SCB */ @@ -153,8 +152,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor scb->next_scb_ptr = ins->the_null_scb; } - spin_lock_irqsave(&chip->reg_lock, flags); - /* update parent first entry in DSP RAM */ cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr); @@ -162,7 +159,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor cs46xx_dsp_spos_update_scb(chip,scb); scb->parent_scb_ptr = NULL; - spin_unlock_irqrestore(&chip->reg_lock, flags); } } @@ -197,9 +193,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * goto _end; #endif - spin_lock_irqsave(&scb->lock, flags); + spin_lock_irqsave(&chip->reg_lock, flags); _dsp_unlink_scb (chip,scb); - spin_unlock_irqrestore(&scb->lock, flags); + spin_unlock_irqrestore(&chip->reg_lock, flags); cs46xx_dsp_proc_free_scb_desc(scb); if (snd_BUG_ON(!scb->scb_symbol)) @@ -207,6 +203,10 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * remove_symbol (chip,scb->scb_symbol); ins->scbs[scb->index].deleted = 1; +#ifdef CONFIG_PM + kfree(ins->scbs[scb->index].data); + ins->scbs[scb->index].data = NULL; +#endif if (scb->index < ins->scb_highest_frag_index) ins->scb_highest_frag_index = scb->index; @@ -1508,20 +1508,17 @@ int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip, chip->dsp_spos_instance->npcm_channels <= 0)) return -EIO; - spin_lock(&pcm_channel->src_scb->lock); - + spin_lock_irqsave(&chip->reg_lock, flags); if (pcm_channel->unlinked) { - spin_unlock(&pcm_channel->src_scb->lock); + spin_unlock_irqrestore(&chip->reg_lock, flags); return -EIO; } - spin_lock_irqsave(&chip->reg_lock, flags); pcm_channel->unlinked = 1; - spin_unlock_irqrestore(&chip->reg_lock, flags); _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb); + spin_unlock_irqrestore(&chip->reg_lock, flags); - spin_unlock(&pcm_channel->src_scb->lock); return 0; } @@ -1533,10 +1530,10 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip, struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb; unsigned long flags; - spin_lock(&pcm_channel->src_scb->lock); + spin_lock_irqsave(&chip->reg_lock, flags); if (pcm_channel->unlinked == 0) { - spin_unlock(&pcm_channel->src_scb->lock); + spin_unlock_irqrestore(&chip->reg_lock, flags); return -EIO; } @@ -1552,8 +1549,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip, snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr); pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb; - spin_lock_irqsave(&chip->reg_lock, flags); - /* update SCB entry in DSP RAM */ cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb); @@ -1562,8 +1557,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip, pcm_channel->unlinked = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); - - spin_unlock(&pcm_channel->src_scb->lock); return 0; } @@ -1596,13 +1589,17 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src) { + unsigned long flags; + if (snd_BUG_ON(!src->parent_scb_ptr)) return -EINVAL; /* mute SCB */ cs46xx_dsp_scb_set_volume (chip,src,0,0); + spin_lock_irqsave(&chip->reg_lock, flags); _dsp_unlink_scb (chip,src); + spin_unlock_irqrestore(&chip->reg_lock, flags); return 0; } -- cgit v1.2.2 From 75d1aeb9d6899b10420d10284e8ea894b2794224 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Dec 2009 11:56:32 +0100 Subject: ALSA: hda - Add Bass Speaker switch for HP dv7 The bass speaker is controlled via GPIO5. Tested-by: Wael Nasreddine Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0bafea9d5106..a4526d008042 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5402,6 +5402,54 @@ static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec, return 0; } +/* HP dv7 bass switch - GPIO5 */ +#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info +static int stac_hp_bass_gpio_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->gpio_data & 0x20); + return 0; +} + +static int stac_hp_bass_gpio_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; + unsigned int gpio_data; + + gpio_data = (spec->gpio_data & ~0x20) | + (ucontrol->value.integer.value[0] ? 0x20 : 0); + if (gpio_data == spec->gpio_data) + return 0; + spec->gpio_data = gpio_data; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + return 1; +} + +static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac_hp_bass_gpio_info, + .get = stac_hp_bass_gpio_get, + .put = stac_hp_bass_gpio_put, +}; + +static int stac_add_hp_bass_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl, + "Bass Speaker Playback Switch", 0)) + return -ENOMEM; + + spec->gpio_mask |= 0x20; + spec->gpio_dir |= 0x20; + spec->gpio_data |= 0x20; + return 0; +} + static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5642,6 +5690,15 @@ again: return err; } + /* enable bass on HP dv7 */ + if (spec->board_config == STAC_HP_DV5) { + unsigned int cap; + cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); + cap &= AC_GPIO_IO_COUNT; + if (cap >= 6) + stac_add_hp_bass_switch(codec); + } + codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; -- cgit v1.2.2 From 21949f00a022e090a7e8bc9a01dfca88273c6146 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Dec 2009 08:31:59 +0100 Subject: ALSA: hda - Fix NID association for capture mixers Fix the wrong implementation of NID <-> kctl mapping for capture mixers introduced by the ocmmit 5b0cb1d850c26893b1468b3a519433a1b7a176be. So far, the driver returns an error at probe. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 26 -------------------------- sound/pci/hda/hda_local.h | 2 -- sound/pci/hda/patch_analog.c | 3 +-- sound/pci/hda/patch_cirrus.c | 12 ++++++++---- sound/pci/hda/patch_cmedia.c | 3 +-- sound/pci/hda/patch_realtek.c | 3 +-- sound/pci/hda/patch_via.c | 3 +-- 7 files changed, 12 insertions(+), 40 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c848ec0f085e..29c90d748c91 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3537,32 +3537,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) } EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); -/** - * snd_hda_add_nids - assign nids to controls from the array - * @codec: the HDA codec - * @kctl: struct snd_kcontrol - * @index: index to kctl - * @nids: the array of hda_nid_t - * @size: count of hda_nid_t items - * - * This helper function assigns NIDs in the given array to a control element. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl, - unsigned int index, hda_nid_t *nids, unsigned int size) -{ - int err; - - for ( ; size > 0; size--, nids++) { - err = snd_hda_add_nid(codec, kctl, index, *nids); - if (err < 0) - return err; - } - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_add_nids); - #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index d505d052972e..7cee364976ff 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -343,8 +343,6 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec, const struct snd_pci_quirk *tbl); int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew); -int snd_hda_add_nids(struct hda_codec *codec, struct snd_kcontrol *kctl, - unsigned int index, hda_nid_t *nids, unsigned int size); /* * unsolicited event handler diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 92b72d4f3984..45ee352df329 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -244,8 +244,7 @@ static int ad198x_build_controls(struct hda_codec *codec) if (!kctl) kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nids(codec, kctl, i, spec->capsrc_nids, - spec->input_mux->num_items); + err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 093cfbb55e9e..7de782a5b8f4 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -753,6 +753,7 @@ static int build_input(struct hda_codec *codec) spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); for (i = 0; i < 2; i++) { struct snd_kcontrol *kctl; + int n; if (!spec->capture_bind[i]) return -ENOMEM; kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); @@ -762,10 +763,13 @@ static int build_input(struct hda_codec *codec) err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; - err = snd_hda_add_nids(codec, kctl, 0, spec->adc_nid, - spec->num_inputs); - if (err < 0) - return err; + for (n = 0; n < AUTO_PIN_LAST; n++) { + if (!spec->adc_nid[n]) + continue; + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[i]); + if (err < 0) + return err; + } } if (spec->num_inputs > 1 && !spec->mic_detect) { diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index cc1c22370a60..ff60908f4554 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -345,8 +345,7 @@ static int cmi9880_build_controls(struct hda_codec *codec) /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nids(codec, kctl, i, spec->adc_nids, - spec->input_mux->num_items); + err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e7cdc6a7d61d..a45199014986 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2551,8 +2551,7 @@ static int alc_build_controls(struct hda_codec *codec) hda_nid_t *nids = spec->capsrc_nids; if (!nids) nids = spec->adc_nids; - err = snd_hda_add_nids(codec, kctl, i, nids, - spec->input_mux->num_items); + err = snd_hda_add_nid(codec, kctl, i, nids[i]); if (err < 0) return err; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index de4839e46762..9ddc37300f6b 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1907,8 +1907,7 @@ static int via_build_controls(struct hda_codec *codec) /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids, - spec->input_mux->num_items); + err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); if (err < 0) return err; } -- cgit v1.2.2 From f62faedbed546f4e0c1ba204999e7c206059f305 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Dec 2009 09:27:51 +0100 Subject: ALSA: hda - Set mixer name after codec patch Postpone the mixer name setup after the codec patch since the codec patch may change the codec name string in itself. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9cfdb771928c..950ee5cfcacf 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1086,11 +1086,6 @@ int snd_hda_codec_configure(struct hda_codec *codec) if (err < 0) return err; } - /* audio codec should override the mixer name */ - if (codec->afg || !*codec->bus->card->mixername) - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); if (is_generic_config(codec)) { err = snd_hda_parse_generic_codec(codec); @@ -1109,6 +1104,11 @@ int snd_hda_codec_configure(struct hda_codec *codec) patched: if (!err && codec->patch_ops.unsol_event) err = init_unsol_queue(codec->bus); + /* audio codec should override the mixer name */ + if (!err && (codec->afg || !*codec->bus->card->mixername)) + snprintf(codec->bus->card->mixername, + sizeof(codec->bus->card->mixername), + "%s %s", codec->vendor_name, codec->chip_name); return err; } EXPORT_SYMBOL_HDA(snd_hda_codec_configure); -- cgit v1.2.2 From 95e70e87533f9d117d369495ee633cb7d18dc802 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Wed, 23 Dec 2009 17:28:45 +0100 Subject: ALSA: hda - Add STAC9205 PCI_QUIRK for Dell Vostro 1700 Signed-off-by: Anisse Astier Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 417fb22ae83c..eeda7beeb57a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2104,6 +2104,7 @@ static unsigned int ref9205_pin_configs[12] = { 10280204 1028021F 10280228 (Dell Vostro 1500) + 10280229 (Dell Vostro 1700) */ static unsigned int dell_9205_m42_pin_configs[12] = { 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, @@ -2189,6 +2190,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { "Dell Inspiron", STAC_9205_DELL_M44), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, "Dell Vostro 1500", STAC_9205_DELL_M42), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229, + "Dell Vostro 1700", STAC_9205_DELL_M42), /* Gateway */ SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD), SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD), -- cgit v1.2.2 From ef18beded8ddbaafdf4914bab209f77e60ae3a18 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 25 Dec 2009 13:14:27 +0800 Subject: ALSA: hda - HDMI sticky stream tag support When we run the following commands in turn (with CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0), speaker-test -Dhw:0,3 -c2 -twav # HDMI speaker-test -Dhw:0,0 -c2 -twav # Analog The second command will produce sound in the analog lineout _as well as_ HDMI sink. The root cause is, device 0 "reuses" the same stream tag that was used by device 3, and the "intelhdmi - sticky stream id" patch leaves the HDMI codec in a functional state. So the HDMI codec happily accepts the audio samples which reuse its stream tag. The proposed solution is to remember the last device each azx_dev was assigned to, and prefer to 1) reuse the azx_dev (and hence the stream tag) the HDMI codec last used 2) or assign a never-used azx_dev for HDMI With this patch and the above two speaker-test commands, HDMI codec will use stream tag 8 and Analog codec will use 5. The stream tag used by HDMI codec won't be reused by others, as long as we don't run out of the 4 playback azx_dev's. The legacy Analog codec will continue to use stream tag 5 because its device id is 0 (this is a bit tricky). Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index ff8ad46cc50e..ec9c348336cc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -356,6 +356,7 @@ struct azx_dev { */ unsigned char stream_tag; /* assigned stream */ unsigned char index; /* stream index */ + int device; /* last device number assigned to */ unsigned int opened :1; unsigned int running :1; @@ -1441,10 +1442,13 @@ static int __devinit azx_codec_configure(struct azx *chip) */ /* assign a stream for the PCM */ -static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream) +static inline struct azx_dev * +azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) { int dev, i, nums; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + struct azx_dev *res = NULL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dev = chip->playback_index_offset; nums = chip->playback_streams; } else { @@ -1453,10 +1457,15 @@ static inline struct azx_dev *azx_assign_device(struct azx *chip, int stream) } for (i = 0; i < nums; i++, dev++) if (!chip->azx_dev[dev].opened) { - chip->azx_dev[dev].opened = 1; - return &chip->azx_dev[dev]; + res = &chip->azx_dev[dev]; + if (res->device == substream->pcm->device) + break; } - return NULL; + if (res) { + res->opened = 1; + res->device = substream->pcm->device; + } + return res; } /* release the assigned stream */ @@ -1505,7 +1514,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) int err; mutex_lock(&chip->open_mutex); - azx_dev = azx_assign_device(chip, substream->stream); + azx_dev = azx_assign_device(chip, substream); if (azx_dev == NULL) { mutex_unlock(&chip->open_mutex); return -EBUSY; -- cgit v1.2.2 From 729d55ba972348234759f8e40abf8de020f0d505 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Dec 2009 22:49:01 +0100 Subject: ALSA: hda - Disable tigger at pin-sensing on AD codecs Analog Device codecs seem to have problems with the triggering of pin-sensing although their pincaps give the trigger requirements. Some reported that constant CPU load on HP laptops with AD codecs. For avoiding this regression, add a flag to codec struct to notify explicitly that the codec doesn't suppot the trigger at pin-sensing. Tested-by: Maciej Rutecki Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 10 ++++++---- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/patch_analog.c | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 950ee5cfcacf..f98b47cd6cfb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1327,11 +1327,13 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); */ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) { - u32 pincap = snd_hda_query_pin_caps(codec, nid); - - if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + u32 pincap; + if (!codec->no_trigger_sense) { + pincap = snd_hda_query_pin_caps(codec, nid); + if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ + snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + } return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 1d541b7f5547..0a770a28e71f 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -817,6 +817,7 @@ struct hda_codec { unsigned int pin_amp_workaround:1; /* pin out-amp takes index * (e.g. Conexant codecs) */ + unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ #ifdef CONFIG_SND_HDA_POWER_SAVE unsigned int power_on :1; /* current (global) power-state */ unsigned int power_transition :1; /* power-state in transition */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 1a36137e13ec..69a941c7b158 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1186,6 +1186,8 @@ static int patch_ad1986a(struct hda_codec *codec) */ spec->multiout.no_share_stream = 1; + codec->no_trigger_sense = 1; + return 0; } @@ -1371,6 +1373,8 @@ static int patch_ad1983(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; + codec->no_trigger_sense = 1; + return 0; } @@ -1813,6 +1817,9 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1981_hp_unsol_event; break; } + + codec->no_trigger_sense = 1; + return 0; } @@ -3118,6 +3125,8 @@ static int patch_ad1988(struct hda_codec *codec) #endif spec->vmaster_nid = 0x04; + codec->no_trigger_sense = 1; + return 0; } @@ -3330,6 +3339,8 @@ static int patch_ad1884(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; + codec->no_trigger_sense = 1; + return 0; } @@ -4287,6 +4298,8 @@ static int patch_ad1884a(struct hda_codec *codec) break; } + codec->no_trigger_sense = 1; + return 0; } @@ -4623,6 +4636,9 @@ static int patch_ad1882(struct hda_codec *codec) spec->mixers[2] = ad1882_6stack_mixers; break; } + + codec->no_trigger_sense = 1; + return 0; } -- cgit v1.2.2 From a252c81a69c4f9a5a8782f33b91bd837e9dcd406 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Dec 2009 22:56:20 +0100 Subject: ALSA: hda - use snd_hda_jack_detect() again in patch_sigmatel.c Use snd_hda_jack_detect() again for jack-sensing. The triggering problem can be worked around with codec->no_trigger_sense flag now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index eeda7beeb57a..2291a8396817 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4453,14 +4453,7 @@ static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; - /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT - * codecs behave wrongly when SET_PIN_SENSE is triggered, although - * the pincap gives TRIG_REQ bit. - */ - if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) & - AC_PINSENSE_PRESENCE) - return 1; - return 0; + return snd_hda_jack_detect(codec, nid); } static void stac92xx_line_out_detect(struct hda_codec *codec, @@ -4962,6 +4955,7 @@ static int patch_stac9200(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; spec->num_pins = ARRAY_SIZE(stac9200_pin_nids); spec->pin_nids = stac9200_pin_nids; @@ -5024,6 +5018,7 @@ static int patch_stac925x(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; spec->num_pins = ARRAY_SIZE(stac925x_pin_nids); spec->pin_nids = stac925x_pin_nids; @@ -5108,6 +5103,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids); @@ -5255,6 +5251,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; spec->digbeep_nid = 0x21; @@ -5418,6 +5415,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; codec->patch_ops = stac92xx_patch_ops; spec->num_pins = STAC92HD71BXX_NUM_PINS; @@ -5661,6 +5659,7 @@ static int patch_stac922x(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; spec->num_pins = ARRAY_SIZE(stac922x_pin_nids); spec->pin_nids = stac922x_pin_nids; @@ -5764,6 +5763,7 @@ static int patch_stac927x(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; codec->slave_dig_outs = stac927x_slave_dig_outs; spec->num_pins = ARRAY_SIZE(stac927x_pin_nids); @@ -5898,6 +5898,7 @@ static int patch_stac9205(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; spec->num_pins = ARRAY_SIZE(stac9205_pin_nids); spec->pin_nids = stac9205_pin_nids; @@ -6053,6 +6054,7 @@ static int patch_stac9872(struct hda_codec *codec) spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; + codec->no_trigger_sense = 1; codec->spec = spec; spec->num_pins = ARRAY_SIZE(stac9872_pin_nids); spec->pin_nids = stac9872_pin_nids; -- cgit v1.2.2 From 043958e602ac2cbf918c0dab1e4e2a7f9751ebf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 26 Dec 2009 10:36:12 +0100 Subject: ALSA: hda - Add more hints for GPIO setup of IDT/STAC codecs gpio_led, gpio_led_polarity and gpio_mute are added now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 247be19e17b8..69dd5a4e52f2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4184,9 +4184,23 @@ static void stac_store_hints(struct hda_codec *codec) p = snd_hda_get_hint(codec, "eapd_mask"); if (p) spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask; + p = snd_hda_get_hint(codec, "gpio_mute"); + if (p) + spec->gpio_mute = simple_strtoul(p, NULL, 0) & spec->gpio_mask; val = snd_hda_get_bool_hint(codec, "eapd_switch"); if (val >= 0) spec->eapd_switch = val; + p = snd_hda_get_hint(codec, "gpio_led_polarity"); + if (p) + spec->gpio_led_polarity = simple_strtoul(p, NULL, 0); + p = snd_hda_get_hint(codec, "gpio_led"); + if (p) { + spec->gpio_led = simple_strtoul(p, NULL, 0); + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + if (spec->gpio_led_polarity) + spec->gpio_data |= spec->gpio_led; + } } static int stac92xx_init(struct hda_codec *codec) -- cgit v1.2.2 From 411fe85c7653f51403c2a6fd9026b0db2ab19478 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 10:25:58 +0100 Subject: ALSA: hda - Don't cache beep controls The beep control verbs don't need to be cached for resume. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 5fe34a8d8c81..ca3c57a5f888 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -42,7 +42,7 @@ static void snd_hda_generate_beep(struct work_struct *work) return; /* generate tone */ - snd_hda_codec_write_cache(codec, beep->nid, 0, + snd_hda_codec_write(codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, beep->tone); } @@ -119,7 +119,7 @@ static void snd_hda_do_detach(struct hda_beep *beep) beep->dev = NULL; cancel_work_sync(&beep->beep_work); /* turn off beep for sure */ - snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + snd_hda_codec_write(beep->codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, 0); } @@ -192,7 +192,7 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) beep->enabled = enable; if (!enable) { /* turn off beep */ - snd_hda_codec_write_cache(beep->codec, beep->nid, 0, + snd_hda_codec_write(beep->codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, 0); } if (beep->mode == HDA_BEEP_MODE_SWREG) { -- cgit v1.2.2 From 54f7190b23080c7ac32078ed6a346bdc591ebef1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 13:27:39 +0100 Subject: ALSA: hda - Fix Oops at reloading beep devices The recent change for supporting dynamic beep device allocation caused a problem resulting in Oops at reloading the driver. Also, it ignores the error from input device registration. This patch fixes the wrong check in snd_hda_detach_beep_device(), and returns an error when the input device registration fails properly. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_beep.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index ca3c57a5f888..e4581a42ace5 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -239,8 +239,12 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) mutex_init(&beep->mutex); if (beep->mode == HDA_BEEP_MODE_ON) { - beep->enabled = 1; - snd_hda_do_register(&beep->register_work); + int err = snd_hda_do_attach(beep); + if (err < 0) { + kfree(beep); + codec->beep = NULL; + return err; + } } return 0; @@ -253,7 +257,7 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) if (beep) { cancel_work_sync(&beep->register_work); cancel_delayed_work(&beep->unregister_work); - if (beep->enabled) + if (beep->dev) snd_hda_do_detach(beep); codec->beep = NULL; kfree(beep); -- cgit v1.2.2 From 92ee6162c48fab24f0676969f0f147fc12f8f21c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 11:18:59 +0100 Subject: ALSA: hda - Add snd_hda_shutup_pins() helper function Add a common helper function for clearing pin controls before suspend. Use the pincfg array instead of looking through all widget tree. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 19 +++++++++++++++++++ sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/patch_sigmatel.c | 12 +----------- 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b3554df740ff..94ae69f20925 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -899,6 +899,25 @@ static void restore_pincfgs(struct hda_codec *codec) } } +/** + * snd_hda_shutup_pins - Shut up all pins + * @codec: the HDA codec + * + * Clear all pin controls to shup up before suspend for avoiding click noise. + * The controls aren't cached so that they can be resumed properly. + */ +void snd_hda_shutup_pins(struct hda_codec *codec) +{ + int i; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + /* use read here for syncing after issuing each verb */ + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } +} +EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); + static void init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size); static void free_hda_cache(struct hda_cache_rec *cache); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0d08ad5bd898..11c4aa8ee996 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -898,6 +898,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid, unsigned int cfg); int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, hda_nid_t nid, unsigned int cfg); /* for hwdep */ +void snd_hda_shutup_pins(struct hda_codec *codec); /* * Mixer diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 69dd5a4e52f2..dc1d9f124578 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4385,18 +4385,8 @@ static void stac92xx_free_kctls(struct hda_codec *codec) static void stac92xx_shutup(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i; - hda_nid_t nid; - /* reset each pin before powering down DAC/ADC to avoid click noise */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wcaps); - if (wid_type == AC_WID_PIN) - snd_hda_codec_read(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } + snd_hda_shutup_pins(codec); if (spec->eapd_mask) stac_gpio_set(codec, spec->gpio_mask, -- cgit v1.2.2 From a4e09aa3cf592d9f084ff4ceb216be40c4c265dc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 11:22:24 +0100 Subject: ALSA: hda - Fix click noises at suspend/free with Realtek codecs Call snd_hda_shutup_pins() at suspend and free for avoiding click noises. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6361e6b3c9c5..cd6d139b4fd5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3693,6 +3693,11 @@ static int alc_build_pcms(struct hda_codec *codec) return 0; } +static inline void alc_shutup(struct hda_codec *codec) +{ + snd_hda_shutup_pins(codec); +} + static void alc_free_kctls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3713,6 +3718,7 @@ static void alc_free(struct hda_codec *codec) if (!spec) return; + alc_shutup(codec); alc_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); @@ -3722,6 +3728,7 @@ static void alc_free(struct hda_codec *codec) static int alc_suspend(struct hda_codec *codec, pm_message_t state) { struct alc_spec *spec = codec->spec; + alc_shutup(codec); if (spec && spec->power_hook) spec->power_hook(codec, 0); return 0; -- cgit v1.2.2 From b82855a0d76ebda1cc14c00040560d77bfa042ce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 11:24:56 +0100 Subject: ALSA: hda - Add sanity check for storing the user-defined pin configs Check whether the given NID is a pin widget before storing the user-defined pin configs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 94ae69f20925..d02ea8926e7e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -824,6 +824,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list, struct hda_pincfg *pin; unsigned int oldcfg; + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return -EINVAL; + oldcfg = snd_hda_codec_get_pincfg(codec, nid); pin = look_up_pincfg(codec, list, nid); if (!pin) { -- cgit v1.2.2 From 014c41fce1bd5cec381e70fc6f58fdfc96cdaf69 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 Dec 2009 13:53:24 +0100 Subject: ALSA: hda - Use strict_strtoul() Rewrite the codes to use strict_strtoul() instead of simple_strtoul(). Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_hwdep.c | 7 ++++-- sound/pci/hda/patch_sigmatel.c | 48 +++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 24 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 40ccb419b6e9..b36919c0d363 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -293,8 +293,11 @@ static ssize_t type##_store(struct device *dev, \ { \ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ struct hda_codec *codec = hwdep->private_data; \ - char *after; \ - codec->type = simple_strtoul(buf, &after, 0); \ + unsigned long val; \ + int err = strict_strtoul(buf, 0, &val); \ + if (err < 0) \ + return err; \ + codec->type = val; \ return count; \ } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index dc1d9f124578..e28c810bc00c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4159,43 +4159,47 @@ static void stac92xx_power_down(struct hda_codec *codec) static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, int enable); +static inline int get_int_hint(struct hda_codec *codec, const char *key, + int *valp) +{ + const char *p; + p = snd_hda_get_hint(codec, key); + if (p) { + unsigned long val; + if (!strict_strtoul(p, 0, &val)) { + *valp = val; + return 1; + } + } + return 0; +} + /* override some hints from the hwdep entry */ static void stac_store_hints(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - const char *p; int val; val = snd_hda_get_bool_hint(codec, "hp_detect"); if (val >= 0) spec->hp_detect = val; - p = snd_hda_get_hint(codec, "gpio_mask"); - if (p) { - spec->gpio_mask = simple_strtoul(p, NULL, 0); + if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) { spec->eapd_mask = spec->gpio_dir = spec->gpio_data = spec->gpio_mask; } - p = snd_hda_get_hint(codec, "gpio_dir"); - if (p) - spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask; - p = snd_hda_get_hint(codec, "gpio_data"); - if (p) - spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask; - p = snd_hda_get_hint(codec, "eapd_mask"); - if (p) - spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask; - p = snd_hda_get_hint(codec, "gpio_mute"); - if (p) - spec->gpio_mute = simple_strtoul(p, NULL, 0) & spec->gpio_mask; + if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir)) + spec->gpio_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_data", &spec->gpio_data)) + spec->gpio_dir &= spec->gpio_mask; + if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask)) + spec->eapd_mask &= spec->gpio_mask; + if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute)) + spec->gpio_mute &= spec->gpio_mask; val = snd_hda_get_bool_hint(codec, "eapd_switch"); if (val >= 0) spec->eapd_switch = val; - p = snd_hda_get_hint(codec, "gpio_led_polarity"); - if (p) - spec->gpio_led_polarity = simple_strtoul(p, NULL, 0); - p = snd_hda_get_hint(codec, "gpio_led"); - if (p) { - spec->gpio_led = simple_strtoul(p, NULL, 0); + get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity); + if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { spec->gpio_mask |= spec->gpio_led; spec->gpio_dir |= spec->gpio_led; if (spec->gpio_led_polarity) -- cgit v1.2.2 From dfb12eeb0f04b37e5eb3858864d074af4ecd2ac7 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 27 Dec 2009 15:48:40 -0500 Subject: ALSA: atiixp: Specify codec for Foxconn RC4107MA-RS2 BugLink: https://bugs.launchpad.net/ubuntu/+bug/498863 This mainboard needs ac97_codec=0. Cc: stable@kernel.org Tested-by: Apoorv Parle Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/atiixp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d6752dff2a44..42b4fbbd8e2b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -297,6 +297,7 @@ static struct pci_device_id snd_atiixp_ids[] = { MODULE_DEVICE_TABLE(pci, snd_atiixp_ids); static struct snd_pci_quirk atiixp_quirks[] __devinitdata = { + SND_PCI_QUIRK(0x105b, 0x0c81, "Foxconn RC4107MA-RS2", 0), SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0), { } /* terminator */ }; -- cgit v1.2.2 From 9980c6209ebc2a02eb3ca51a4fae1e17f645c868 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sun, 27 Dec 2009 22:26:47 +0100 Subject: ALSA: test off by one in setsamplerate() With `while (i++ < MAX_WRITE_RETRY)' i reaches MAX_WRITE_RETRY + 1 after the loop Signed-off-by: Roel Kluin Signed-off-by: Takashi Iwai --- sound/pci/riptide/riptide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index b5ca02e2038c..e66ef2b69b5d 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1058,7 +1058,7 @@ setsamplerate(struct cmdif *cif, unsigned char *intdec, unsigned int rate) rptr.retwords[2] != M && rptr.retwords[3] != N && i++ < MAX_WRITE_RETRY); - if (i == MAX_WRITE_RETRY) { + if (i > MAX_WRITE_RETRY) { snd_printdd("sent samplerate %d: %d failed\n", *intdec, rate); return -EIO; -- cgit v1.2.2 From ea52bf260ecbb175339af3178c15788df21b7516 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 27 Dec 2009 18:48:29 -0500 Subject: ALSA: hda: Add powerdown for Analog Devices HDA codecs This patch ports powerdown fixes to AD198x. Currently we only turn off Front and HP for suspend, but this is easily extended for additional nids. Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 45ee352df329..cecd3c108990 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -441,6 +441,11 @@ static int ad198x_build_pcms(struct hda_codec *codec) return 0; } +static inline void ad198x_shutup(struct hda_codec *codec) +{ + snd_hda_shutup_pins(codec); +} + static void ad198x_free_kctls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -454,6 +459,46 @@ static void ad198x_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, + hda_nid_t hp) +{ + struct ad198x_spec *spec = codec->spec; + snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, + !spec->inv_eapd ? 0x00 : 0x02); + snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, + !spec->inv_eapd ? 0x00 : 0x02); +} + +static void ad198x_power_eapd(struct hda_codec *codec) +{ + /* We currently only handle front, HP */ + switch (codec->vendor_id) { + case 0x11d41882: + case 0x11d4882a: + case 0x11d41884: + case 0x11d41984: + case 0x11d41883: + case 0x11d4184a: + case 0x11d4194a: + case 0x11d4194b: + ad198x_power_eapd_write(codec, 0x12, 0x11); + break; + case 0x11d41981: + case 0x11d41983: + ad198x_power_eapd_write(codec, 0x05, 0x06); + break; + case 0x11d41986: + ad198x_power_eapd_write(codec, 0x1b, 0x1a); + break; + case 0x11d41988: + case 0x11d4198b: + case 0x11d4989a: + case 0x11d4989b: + ad198x_power_eapd_write(codec, 0x29, 0x22); + break; + } +} + static void ad198x_free(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -461,11 +506,29 @@ static void ad198x_free(struct hda_codec *codec) if (!spec) return; + ad198x_shutup(codec); ad198x_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); } +#ifdef SND_HDA_NEEDS_RESUME +static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) +{ + ad198x_shutup(codec); + ad198x_power_eapd(codec); + return 0; +} + +static int ad198x_resume(struct hda_codec *codec) +{ + ad198x_init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + return 0; +} +#endif + static struct hda_codec_ops ad198x_patch_ops = { .build_controls = ad198x_build_controls, .build_pcms = ad198x_build_pcms, @@ -474,6 +537,11 @@ static struct hda_codec_ops ad198x_patch_ops = { #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = ad198x_check_power_status, #endif +#ifdef SND_HDA_NEEDS_RESUME + .suspend = ad198x_suspend, + .resume = ad198x_resume, +#endif + .reboot_notify = ad198x_shutup, }; -- cgit v1.2.2 From c97259df3f2e163c72f4d0685c61fb2e026dc989 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 27 Dec 2009 18:52:08 -0500 Subject: ALSA: hda: Refactor powerdown for Realtek HDA codecs This patch converts the alc889 Aspire-specific powerdown to a generic one. Like the previous effort, it currently only handles Front and PCM but can be easily extended to cover other nids. The existing hook for alc889 Aspire-specific remains enabled. Upon further testing, I've added its use for ALC861_AUTO as well. Following patches will enable them for other quirks. Tested-by: Dr. David Alan Gilbert Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 60 +++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cd6d139b4fd5..141ff446104a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -338,7 +338,7 @@ struct alc_spec { void (*init_hook)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); #ifdef CONFIG_SND_HDA_POWER_SAVE - void (*power_hook)(struct hda_codec *codec, int power); + void (*power_hook)(struct hda_codec *codec); #endif /* for pin sensing */ @@ -391,7 +391,7 @@ struct alc_config_preset { void (*init_hook)(struct hda_codec *); #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_amp_list *loopbacks; - void (*power_hook)(struct hda_codec *codec, int power); + void (*power_hook)(struct hda_codec *codec); #endif }; @@ -1835,16 +1835,6 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[2] = 0x1b; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -static void alc889_power_eapd(struct hda_codec *codec, int power) -{ - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); - snd_hda_codec_write(codec, 0x15, 0, - AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); -} -#endif - /* * ALC880 3-stack model * @@ -3725,12 +3715,40 @@ static void alc_free(struct hda_codec *codec) } #ifdef CONFIG_SND_HDA_POWER_SAVE +static void alc_power_eapd(struct hda_codec *codec) +{ + /* We currently only handle front, HP */ + switch (codec->vendor_id) { + case 0x10ec0260: + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + break; + case 0x10ec0262: + case 0x10ec0267: + case 0x10ec0268: + case 0x10ec0269: + case 0x10ec0272: + case 0x10ec0660: + case 0x10ec0662: + case 0x10ec0663: + case 0x10ec0862: + case 0x10ec0889: + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + snd_hda_codec_write(codec, 0x15, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + break; + } +} + static int alc_suspend(struct hda_codec *codec, pm_message_t state) { struct alc_spec *spec = codec->spec; alc_shutup(codec); if (spec && spec->power_hook) - spec->power_hook(codec, 0); + spec->power_hook(codec); return 0; } #endif @@ -3738,16 +3756,9 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state) #ifdef SND_HDA_NEEDS_RESUME static int alc_resume(struct hda_codec *codec) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - struct alc_spec *spec = codec->spec; -#endif codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (spec && spec->power_hook) - spec->power_hook(codec, 1); -#endif return 0; } #endif @@ -3767,6 +3778,7 @@ static struct hda_codec_ops alc_patch_ops = { .suspend = alc_suspend, .check_power_status = alc_check_power_status, #endif + .reboot_notify = alc_shutup, }; @@ -9547,7 +9559,7 @@ static struct alc_config_preset alc882_presets[] = { .setup = alc889_acer_aspire_8930g_setup, .init_hook = alc_automute_amp, #ifdef CONFIG_SND_HDA_POWER_SAVE - .power_hook = alc889_power_eapd, + .power_hook = alc_power_eapd, #endif }, [ALC888_ACER_ASPIRE_7730G] = { @@ -14984,8 +14996,12 @@ static int patch_alc861(struct hda_codec *codec) spec->vmaster_nid = 0x03; codec->patch_ops = alc_patch_ops; - if (board_config == ALC861_AUTO) + if (board_config == ALC861_AUTO) { spec->init_hook = alc861_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->power_hook = alc_power_eapd; +#endif + } #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc861_loopbacks; -- cgit v1.2.2 From dd3533eca859a6debb1565503ec03e68354e08e0 Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Fri, 1 Jan 2010 19:05:43 +0100 Subject: ALSA: ac97_codec: merge WM9703 and WM9705 ops The WM9705 and WM9703 ops are the same actually so use the same code for both. Signed-off-by: Krzysztof Helt Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 139cf3b2b9d7..e288a5595f34 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -544,25 +544,10 @@ static int patch_wolfson04(struct snd_ac97 * ac97) return 0; } -static int patch_wolfson_wm9705_specific(struct snd_ac97 * ac97) -{ - int err, i; - for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0) - return err; - } - snd_ac97_write_cache(ac97, 0x72, 0x0808); - return 0; -} - -static struct snd_ac97_build_ops patch_wolfson_wm9705_ops = { - .build_specific = patch_wolfson_wm9705_specific, -}; - static int patch_wolfson05(struct snd_ac97 * ac97) { /* WM9705, WM9710 */ - ac97->build_ops = &patch_wolfson_wm9705_ops; + ac97->build_ops = &patch_wolfson_wm9703_ops; #ifdef CONFIG_TOUCHSCREEN_WM9705 /* WM9705 touchscreen uses AUX and VIDEO for touch */ ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX; -- cgit v1.2.2 From cd9d95a55550555da8e587ead9cbba5f98a371a3 Mon Sep 17 00:00:00 2001 From: Ken Prox Date: Fri, 8 Jan 2010 09:01:47 +0100 Subject: ALSA: hda - conexant - Fixed microphone mixer for HP Compaq Presario F700 Added patch for Hewlett-Packard Company Device Subsystem id - 103c:30ea. Signed-off-by: Ken Prox Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_conexant.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 1ab2958a290b..b20c640f7502 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1720,6 +1720,22 @@ static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5051_f700_mixers[] = { + HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + static struct hda_verb cxt5051_init_verbs[] = { /* Line in, Mic */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, @@ -1810,6 +1826,32 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { { } /* end */ }; +static struct hda_verb cxt5051_f700_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, + { } /* end */ +}; + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { @@ -1829,6 +1871,7 @@ enum { CXT5051_HP, /* no docking */ CXT5051_HP_DV6736, /* HP without mic switch */ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ + CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_MODELS }; @@ -1837,6 +1880,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_HP] = "hp", [CXT5051_HP_DV6736] = "hp-dv6736", [CXT5051_LENOVO_X200] = "lenovo-x200", + [CXT5051_F700] = "hp 700" }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { @@ -1846,6 +1890,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = { CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), + SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), {} }; @@ -1896,6 +1941,11 @@ static int patch_cxt5051(struct hda_codec *codec) case CXT5051_LENOVO_X200: spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; break; + case CXT5051_F700: + spec->init_verbs[0] = cxt5051_f700_init_verbs; + spec->mixers[0] = cxt5051_f700_mixers; + spec->no_auto_mic = 1; + break; } return 0; -- cgit v1.2.2 From 75f8991d0e6969407d51501d5a0537f104075c99 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 7 Jan 2010 13:46:25 +0100 Subject: ALSA: hda - Configure XO-1.5 microphones at capture time The XO-1.5 has a microphone LED designed to indicate to the user when something is being recorded. This light is controlled by the microphone bias voltage and it is currently coming on all the time. This patch defers the microphone port configuration until when recording is actually taking place, fixing the behaviour of the LED. Signed-off-by: Daniel Drake Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_conexant.c | 125 ++++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 40 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 01e46ba72690..3521f33d43c3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -111,8 +111,12 @@ struct conexant_spec { unsigned int dell_automute; unsigned int port_d_mode; - unsigned char ext_mic_bias; unsigned int dell_vostro; + + unsigned int ext_mic_present; + unsigned int recording; + void (*capture_prepare)(struct hda_codec *codec); + void (*capture_cleanup)(struct hda_codec *codec); }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -185,6 +189,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; + if (spec->capture_prepare) + spec->capture_prepare(codec); snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], stream_tag, 0, format); return 0; @@ -196,6 +202,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, { struct conexant_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); + if (spec->capture_cleanup) + spec->capture_cleanup(codec); return 0; } @@ -2016,53 +2024,53 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, return 1; } -/* toggle input of built-in and mic jack appropriately */ -static void cxt5066_automic(struct hda_codec *codec) +/* OLPC defers mic widget control until when capture is started because the + * microphone LED comes on as soon as these settings are put in place. if we + * did this before recording, it would give the false indication that recording + * is happening when it is not. */ +static void cxt5066_olpc_select_mic(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - struct hda_verb ext_mic_present[] = { - /* enable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, - - /* switch to external mic input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 0}, + if (!spec->recording) + return; - /* disable internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; - static struct hda_verb ext_mic_absent[] = { - /* enable internal mic, port C */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /* external mic, port B */ + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); - /* switch to internal mic input */ - {0x17, AC_VERB_SET_CONNECT_SEL, 1}, + /* internal mic, port C */ + snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + spec->ext_mic_present ? 0 : PIN_VREF80); +} - /* disable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, - {} - }; +/* toggle input of built-in and mic jack appropriately */ +static void cxt5066_olpc_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; unsigned int present; - present = snd_hda_jack_detect(codec, 0x1a); - if (present) { + present = snd_hda_codec_read(codec, 0x1a, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + if (present) snd_printdd("CXT5066: external microphone detected\n"); - snd_hda_sequence_write(codec, ext_mic_present); - } else { + else snd_printdd("CXT5066: external microphone absent\n"); - snd_hda_sequence_write(codec, ext_mic_absent); - } + + snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL, + present ? 0 : 1); + spec->ext_mic_present = !!present; + + cxt5066_olpc_select_mic(codec); } /* toggle input of built-in digital mic and mic jack appropriately */ static void cxt5066_vostro_automic(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; unsigned int present; struct hda_verb ext_mic_present[] = { /* enable external mic, port B */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* switch to external mic input */ {0x17, AC_VERB_SET_CONNECT_SEL, 0}, @@ -2113,7 +2121,7 @@ static void cxt5066_hp_automute(struct hda_codec *codec) } /* unsolicited event for jack sensing */ -static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) +static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) { snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { @@ -2121,7 +2129,7 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res) cxt5066_hp_automute(codec); break; case CONEXANT_MIC_EVENT: - cxt5066_automic(codec); + cxt5066_olpc_automic(codec); break; } } @@ -2197,6 +2205,31 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, return 1; } +static void cxt5066_olpc_capture_prepare(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + /* mark as recording and configure the microphone widget so that the + * recording LED comes on. */ + spec->recording = 1; + cxt5066_olpc_select_mic(codec); +} + +static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + const struct hda_verb disable_mics[] = { + /* disable external mic, port B */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* disble internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {}, + }; + + snd_hda_sequence_write(codec, disable_mics); + spec->recording = 0; +} + static struct hda_input_mux cxt5066_capture_source = { .num_items = 4, .items = { @@ -2347,10 +2380,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ /* Port B: external microphone */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port C: internal microphone */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port D: unused */ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, @@ -2479,12 +2512,19 @@ static int cxt5066_init(struct hda_codec *codec) cxt5066_hp_automute(codec); if (spec->dell_vostro) cxt5066_vostro_automic(codec); - else - cxt5066_automic(codec); } return 0; } +static int cxt5066_olpc_init(struct hda_codec *codec) +{ + snd_printdd("CXT5066: init\n"); + conexant_init(codec); + cxt5066_hp_automute(codec); + cxt5066_olpc_automic(codec); + return 0; +} + enum { CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ @@ -2521,7 +2561,7 @@ static int patch_cxt5066(struct hda_codec *codec) codec->spec = spec; codec->patch_ops = conexant_patch_ops; - codec->patch_ops.init = cxt5066_init; + codec->patch_ops.init = conexant_init; spec->dell_automute = 0; spec->multiout.max_channels = 2; @@ -2534,7 +2574,6 @@ static int patch_cxt5066(struct hda_codec *codec) spec->input_mux = &cxt5066_capture_source; spec->port_d_mode = PIN_HP; - spec->ext_mic_bias = PIN_VREF80; spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5066_init_verbs; @@ -2561,20 +2600,26 @@ static int patch_cxt5066(struct hda_codec *codec) spec->dell_automute = 1; break; case CXT5066_OLPC_XO_1_5: - codec->patch_ops.unsol_event = cxt5066_unsol_event; + codec->patch_ops.init = cxt5066_olpc_init; + codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; spec->init_verbs[0] = cxt5066_init_verbs_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->port_d_mode = 0; - spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS; /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; /* input source automatically selected */ spec->input_mux = NULL; + + /* our capture hooks which allow us to turn on the microphone LED + * at the right time */ + spec->capture_prepare = cxt5066_olpc_capture_prepare; + spec->capture_cleanup = cxt5066_olpc_capture_cleanup; break; case CXT5066_DELL_VOSTO: + codec->patch_ops.init = cxt5066_init; codec->patch_ops.unsol_event = cxt5066_vostro_event; spec->init_verbs[0] = cxt5066_init_verbs_vostro; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; -- cgit v1.2.2 From c4cfe66c4c2d5a91b3734ffb4e2bad0badd5c874 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 7 Jan 2010 13:47:04 +0100 Subject: ALSA: hda - support OLPC XO-1.5 DC input The XO's audio hardware is wired up to allow DC sensors (e.g. light sensors, thermistors, etc) to be plugged in through the microphone jack. Add sound mixer controls to allow this mode to be enabled and tweaked. Signed-off-by: Daniel Drake Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_conexant.c | 213 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 23 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 3521f33d43c3..685015a53292 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -117,6 +117,16 @@ struct conexant_spec { unsigned int recording; void (*capture_prepare)(struct hda_codec *codec); void (*capture_cleanup)(struct hda_codec *codec); + + /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors) + * through the microphone jack. + * When the user enables this through a mixer switch, both internal and + * external microphones are disabled. Gain is fixed at 0dB. In this mode, + * we also allow the bias to be configured through a separate mixer + * control. */ + unsigned int dc_enable; + unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ + unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -2024,6 +2034,26 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol, return 1; } +static const struct hda_input_mux cxt5066_olpc_dc_bias = { + .num_items = 3, + .items = { + { "Off", PIN_IN }, + { "50%", PIN_VREF50 }, + { "80%", PIN_VREF80 }, + }, +}; + +static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + /* Even though port F is the DC input, the bias is controlled on port B. + * we also leave that port as an active input (but unselected) in DC mode + * just in case that is necessary to make the bias setting take effect. */ + return snd_hda_codec_write_cache(codec, 0x1a, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); +} + /* OLPC defers mic widget control until when capture is started because the * microphone LED comes on as soon as these settings are put in place. if we * did this before recording, it would give the false indication that recording @@ -2034,6 +2064,27 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec) if (!spec->recording) return; + if (spec->dc_enable) { + /* in DC mode we ignore presence detection and just use the jack + * through our special DC port */ + const struct hda_verb enable_dc_mode[] = { + /* disble internal mic, port C */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* enable DC capture, port F */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {}, + }; + + snd_hda_sequence_write(codec, enable_dc_mode); + /* port B input disabled (and bias set) through the following call */ + cxt5066_set_olpc_dc_bias(codec); + return; + } + + /* disable DC (port F) */ + snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + /* external mic, port B */ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); @@ -2049,6 +2100,9 @@ static void cxt5066_olpc_automic(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int present; + if (spec->dc_enable) /* don't do presence detection in DC mode */ + return; + present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; if (present) @@ -2123,13 +2177,16 @@ static void cxt5066_hp_automute(struct hda_codec *codec) /* unsolicited event for jack sensing */ static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res) { + struct conexant_spec *spec = codec->spec; snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26); switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5066_hp_automute(codec); break; case CONEXANT_MIC_EVENT: - cxt5066_olpc_automic(codec); + /* ignore mic events in DC mode; we're always using the jack */ + if (!spec->dc_enable) + cxt5066_olpc_automic(codec); break; } } @@ -2159,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = { }, }; +static int cxt5066_set_mic_boost(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + return snd_hda_codec_write_cache(codec, 0x17, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | + cxt5066_analog_mic_boost.items[spec->mic_boost].index); +} + static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2169,15 +2235,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int val; - hda_nid_t nid = kcontrol->private_value & 0xff; - int inout = (kcontrol->private_value & 0x100) ? - AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT; - - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, inout); - - ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN; + struct conexant_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->mic_boost; return 0; } @@ -2185,23 +2244,101 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; unsigned int idx; - hda_nid_t nid = kcontrol->private_value & 0xff; - int inout = (kcontrol->private_value & 0x100) ? - AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT; + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + + spec->mic_boost = idx; + if (!spec->dc_enable) + cxt5066_set_mic_boost(codec); + return 1; +} + +static void cxt5066_enable_dc(struct hda_codec *codec) +{ + const struct hda_verb enable_dc_mode[] = { + /* disable gain */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* switch to DC input */ + {0x17, AC_VERB_SET_CONNECT_SEL, 3}, + {} + }; + + /* configure as input source */ + snd_hda_sequence_write(codec, enable_dc_mode); + cxt5066_olpc_select_mic(codec); /* also sets configured bias */ +} + +static void cxt5066_disable_dc(struct hda_codec *codec) +{ + /* reconfigure input source */ + cxt5066_set_mic_boost(codec); + /* automic also selects the right mic if we're recording */ + cxt5066_olpc_automic(codec); +} + +static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + ucontrol->value.integer.value[0] = spec->dc_enable; + return 0; +} - if (!imux->num_items) +static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + int dc_enable = !!ucontrol->value.integer.value[0]; + + if (dc_enable == spec->dc_enable) return 0; + + spec->dc_enable = dc_enable; + if (dc_enable) + cxt5066_enable_dc(codec); + else + cxt5066_disable_dc(codec); + + return 1; +} + +static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo); +} + +static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->dc_input_bias; + return 0; +} + +static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + const struct hda_input_mux *imux = &cxt5066_analog_mic_boost; + unsigned int idx; + idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout | - imux->items[idx].index); - + spec->dc_input_bias = idx; + if (spec->dc_enable) + cxt5066_set_olpc_dc_bias(codec); return 1; } @@ -2223,6 +2360,9 @@ static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec) /* disble internal mic, port C */ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* disable DC capture, port F */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {}, }; @@ -2282,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = { {} }; +static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Mode Enable Switch", + .info = snd_ctl_boolean_mono_info, + .get = cxt5066_olpc_dc_get, + .put = cxt5066_olpc_dc_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC Input Bias Enum", + .info = cxt5066_olpc_dc_bias_enum_info, + .get = cxt5066_olpc_dc_bias_enum_get, + .put = cxt5066_olpc_dc_bias_enum_put, + }, + {} +}; + static struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2294,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Ext Mic Boost Capture Enum", + .name = "Analog Mic Boost Capture Enum", .info = cxt5066_mic_boost_mux_enum_info, .get = cxt5066_mic_boost_mux_enum_get, .put = cxt5066_mic_boost_mux_enum_put, - .private_value = 0x17, }, HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others), @@ -2392,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = { {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - /* Port F: unused */ + /* Port F: external DC input through microphone port */ {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, /* Port G: internal speakers */ @@ -2513,15 +2670,22 @@ static int cxt5066_init(struct hda_codec *codec) if (spec->dell_vostro) cxt5066_vostro_automic(codec); } + cxt5066_set_mic_boost(codec); return 0; } static int cxt5066_olpc_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; snd_printdd("CXT5066: init\n"); conexant_init(codec); cxt5066_hp_automute(codec); - cxt5066_olpc_automic(codec); + if (!spec->dc_enable) { + cxt5066_set_mic_boost(codec); + cxt5066_olpc_automic(codec); + } else { + cxt5066_enable_dc(codec); + } return 0; } @@ -2604,8 +2768,10 @@ static int patch_cxt5066(struct hda_codec *codec) codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event; spec->init_verbs[0] = cxt5066_init_verbs_olpc; spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc; spec->mixers[spec->num_mixers++] = cxt5066_mixers; spec->port_d_mode = 0; + spec->mic_boost = 3; /* default 30dB gain */ /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; @@ -2627,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers; spec->port_d_mode = 0; spec->dell_vostro = 1; + spec->mic_boost = 3; /* default 30dB gain */ snd_hda_attach_beep_device(codec, 0x13); /* no S/PDIF out */ -- cgit v1.2.2 From af9a75dd1a1f8a9aa406466cc8bb16208120488a Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sat, 9 Jan 2010 01:22:29 -0500 Subject: ALSA: ac97: Add Dell Dimension 2400 to Headphone/Line Jack Sense blacklist This model needs both 'Headphone Jack Sense' and 'Line Jack Sense' muted for audible playback, so just add it to the ad1981 jack sense blacklist. Cc: stable@kernel.org Tested-by: Pete Signed-off-by: Daniel T Chen Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_patch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 139cf3b2b9d7..d9266bae2849 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1870,6 +1870,7 @@ static unsigned int ad1981_jacks_blacklist[] = { 0x10140554, /* Thinkpad T42p/R50p */ 0x10140567, /* Thinkpad T43p 2668-G7U */ 0x10140581, /* Thinkpad X41-2527 */ + 0x10280160, /* Dell Dimension 2400 */ 0x104380b0, /* Asus A7V8X-MX */ 0x11790241, /* Toshiba Satellite A-15 S127 */ 0x144dc01a, /* Samsung NP-X20C004/SEG */ -- cgit v1.2.2 From c68db7175f4dcb3d5789bb50bea6376fb81f87fe Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Sun, 10 Jan 2010 17:21:14 +0100 Subject: ALSA: ac97: add AC97 STMicroelectronics' codecs Add the STMicroelectronics ST7597 codec and an unknown codec from the same manufacturer found on the Creative SB 128 card (CT4810). Signed-off-by: Krzysztof Helt Signed-off-by: Jaroslav Kysela --- sound/pci/ac97/ac97_codec.c | 10 ++++++++++ sound/pci/ac97/ac97_id.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index c11920623009..a7630e9edf8a 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -83,6 +83,7 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = { { 0x4e534300, 0xffffff00, "National Semiconductor", NULL, NULL }, { 0x50534300, 0xffffff00, "Philips", NULL, NULL }, { 0x53494c00, 0xffffff00, "Silicon Laboratory", NULL, NULL }, +{ 0x53544d00, 0xffffff00, "STMicroelectronics", NULL, NULL }, { 0x54524100, 0xffffff00, "TriTech", NULL, NULL }, { 0x54584e00, 0xffffff00, "Texas Instruments", NULL, NULL }, { 0x56494100, 0xffffff00, "VIA Technologies", NULL, NULL }, @@ -161,6 +162,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { { 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix { 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL }, { 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH }, +{ 0x53544d02, 0xffffffff, "ST7597", NULL, NULL }, { 0x54524102, 0xffffffff, "TR28022", NULL, NULL }, { 0x54524103, 0xffffffff, "TR28023", NULL, NULL }, { 0x54524106, 0xffffffff, "TR28026", NULL, NULL }, @@ -213,6 +215,14 @@ static int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg) { /* filter some registers for buggy codecs */ switch (ac97->id) { + case AC97_ID_ST_AC97_ID4: + if (reg == 0x08) + return 0; + /* fall through */ + case AC97_ID_ST7597: + if (reg == 0x22 || reg == 0x7a) + return 1; + /* fall through */ case AC97_ID_AK4540: case AC97_ID_AK4542: if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c) diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h index c129492c82b3..d603147c4a96 100644 --- a/sound/pci/ac97/ac97_id.h +++ b/sound/pci/ac97/ac97_id.h @@ -62,3 +62,5 @@ #define AC97_ID_CM9761_78 0x434d4978 #define AC97_ID_CM9761_82 0x434d4982 #define AC97_ID_CM9761_83 0x434d4983 +#define AC97_ID_ST7597 0x53544d02 +#define AC97_ID_ST_AC97_ID4 0x53544d04 -- cgit v1.2.2 From 9c0afc861a7228f718cb6a79fa7f9d46bf9ff300 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Jan 2010 14:00:11 +0100 Subject: ALSA: hda - Fix ALC861-VD capture source mixer The capture source or input source mixer element wasn't created properly for ALC861-VD codec due to the wrong NID passed to alc_auto_create_input_ctls(). References: Novell bnc#568305 http://bugzilla.novell.com/show_bug.cgi?id=568305 Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c7465053d6bb..e3caa78ccd54 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -15493,7 +15493,7 @@ static struct alc_config_preset alc861vd_presets[] = { static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0); + return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x22, 0); } -- cgit v1.2.2 From d2f2fcd2541bae004db7f4798ffd9d2cb75ae817 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Tue, 12 Jan 2010 17:03:35 -0800 Subject: ALSA: hda_intel: ALSA HD Audio patch for Intel Cougar Point DeviceIDs This patch adds the Intel Cougar Point (PCH) HD Audio Controller DeviceIDs. Signed-off-by: Seth Heasley Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1f516e668d88..6d331c4cf185 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -125,6 +125,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH9}," "{Intel, ICH10}," "{Intel, PCH}," + "{Intel, CPT}," "{Intel, SCH}," "{ATI, SB450}," "{ATI, SB600}," @@ -2677,6 +2678,8 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, /* PCH */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH }, + /* CPT */ + { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_ICH }, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, /* ATI SB 450/600 */ -- cgit v1.2.2 From 4dee8baa18d611b6dc854e1cc193550ff6f687be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 13 Jan 2010 17:20:08 +0100 Subject: ALSA: hda - Fix Toshiba NB20x quirk entry The alc664-mode4 model doesn't seem to fit with Toshiba NB205 correctly. NB205 uses the pin 0x17 connected with the mixer 0x0f for the speaker output, which isn't controlled by mode4 model at all. Rather model=auto works fine as is on the latest driver, so let it back again. Tested-by: Nickolas Lloyd Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e3caa78ccd54..bff60cea7777 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -17251,7 +17251,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", ALC662_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB200", ALC663_ASUS_MODE4), + SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", ALC662_3ST_6ch_DIG), -- cgit v1.2.2 From a76221d47ef2b73ff16c0fef00a784026308ea02 Mon Sep 17 00:00:00 2001 From: Alex Murray Date: Wed, 13 Jan 2010 23:15:03 +1030 Subject: ALSA: hda - Improved MacBook (Pro) 5,1 / 5,2 support This patch adds support for automatically muting the speakers when headphones are inserted, as well as relabelling the headphone widgets from the non-standard "HP" to the standard "Headphone" for the mb5 model. Signed-off-by: Alex Murray Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bff60cea7777..11b989bacd3c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7094,8 +7094,8 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = { HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("HP Playback Volume", 0x0f, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("HP Playback Switch", 0x0f, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, 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, 0x01, HDA_INPUT), @@ -7496,6 +7496,7 @@ static struct hda_verb alc885_mb5_init_verbs[] = { {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, /* Front Mic pin: input vref at 80% */ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, @@ -7680,6 +7681,27 @@ static void alc885_mbp3_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; } +static void alc885_mb5_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + +} + +static void alc885_mb5_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Headphone insertion or removal. */ + if ((res >> 26) == ALC880_HP_EVENT) + alc885_mb5_automute(codec); +} + static void alc885_imac91_automute(struct hda_codec *codec) { unsigned int present; @@ -9126,6 +9148,8 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &mb5_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc885_mb5_unsol_event, + .init_hook = alc885_mb5_automute, }, [ALC885_MACPRO] = { .mixers = { alc882_macpro_mixer }, -- cgit v1.2.2 From 408bffd01cfcda2907b07fb86b3666e3db86fd82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Jan 2010 09:19:46 +0100 Subject: ALSA: ctxfi - Add subsystem option Added a new option "subsystem" to override the PCI SSID for identifying the card type. Signed-off-by: Takashi Iwai --- sound/pci/ctxfi/ctatc.c | 23 +++++++++++++++-------- sound/pci/ctxfi/ctatc.h | 2 +- sound/pci/ctxfi/xfi.c | 5 ++++- 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index cb65bd0dd35b..903594e6ed79 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -1225,10 +1225,11 @@ static int atc_dev_free(struct snd_device *dev) return ct_atc_destroy(atc); } -static int __devinit atc_identify_card(struct ct_atc *atc) +static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid) { const struct snd_pci_quirk *p; const struct snd_pci_quirk *list; + u16 vendor_id, device_id; switch (atc->chip_type) { case ATC20K1: @@ -1242,13 +1243,19 @@ static int __devinit atc_identify_card(struct ct_atc *atc) default: return -ENOENT; } - p = snd_pci_quirk_lookup(atc->pci, list); + if (ssid) { + vendor_id = ssid >> 16; + device_id = ssid & 0xffff; + } else { + vendor_id = atc->pci->subsystem_vendor; + device_id = atc->pci->subsystem_device; + } + p = snd_pci_quirk_lookup_id(vendor_id, device_id, list); if (p) { if (p->value < 0) { printk(KERN_ERR "ctxfi: " "Device %04x:%04x is black-listed\n", - atc->pci->subsystem_vendor, - atc->pci->subsystem_device); + vendor_id, device_id); return -ENOENT; } atc->model = p->value; @@ -1261,8 +1268,7 @@ static int __devinit atc_identify_card(struct ct_atc *atc) atc->model_name = ct_subsys_name[atc->model]; snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", atc->chip_name, atc->model_name, - atc->pci->subsystem_vendor, - atc->pci->subsystem_device); + vendor_id, device_id); return 0; } @@ -1636,7 +1642,8 @@ static struct ct_atc atc_preset __devinitdata = { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, - int chip_type, struct ct_atc **ratc) + int chip_type, unsigned int ssid, + struct ct_atc **ratc) { struct ct_atc *atc; static struct snd_device_ops ops = { @@ -1662,7 +1669,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, mutex_init(&atc->atc_mutex); /* Find card model */ - err = atc_identify_card(atc); + err = atc_identify_card(atc, ssid); if (err < 0) { printk(KERN_ERR "ctatc: Card not recognised\n"); goto error1; diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h index 9fd8a5708943..7167c0185d52 100644 --- a/sound/pci/ctxfi/ctatc.h +++ b/sound/pci/ctxfi/ctatc.h @@ -148,7 +148,7 @@ struct ct_atc { int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, unsigned int rsr, unsigned int msr, int chip_type, - struct ct_atc **ratc); + unsigned int subsysid, struct ct_atc **ratc); int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc); #endif /* CTATC_H */ diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index 76541748e7bc..ed44ed788b60 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -32,6 +32,7 @@ module_param(multiple, uint, S_IRUGO); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static unsigned int subsystem[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver"); @@ -39,6 +40,8 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); +module_param_array(subsystem, int, NULL, 0444); +MODULE_PARM_DESC(subsystem, "Override subsystem ID for Creative X-Fi driver"); static struct pci_device_id ct_pci_dev_ids[] = { /* only X-Fi is supported, so... */ @@ -85,7 +88,7 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) multiple = 2; } err = ct_atc_create(card, pci, reference_rate, multiple, - pci_id->driver_data, &atc); + pci_id->driver_data, subsystem[dev], &atc); if (err < 0) goto error; -- cgit v1.2.2 From c7a8eb103248a110cdbe0530d8c5ce987f099eee Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Jan 2010 12:39:02 +0100 Subject: ALSA: hda - Fix missing capture mixer for ALC861/660 codecs The capture-related mixer elements are missing with ALC861/ALC660 codecs when quirks are present, due to missing call of set_capture_mixer(). Reference: Novell bnc#567340 http://bugzilla.novell.com/show_bug.cgi?id=567340 Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 11b989bacd3c..abae1007cea2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14879,6 +14879,8 @@ static int patch_alc861(struct hda_codec *codec) spec->stream_digital_playback = &alc861_pcm_digital_playback; spec->stream_digital_capture = &alc861_pcm_digital_capture; + if (!spec->cap_mixer) + set_capture_mixer(codec); set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); spec->vmaster_nid = 0x03; -- cgit v1.2.2 From d38cce7046cfd0011f69d5dcf6a22525438154f6 Mon Sep 17 00:00:00 2001 From: Kunal Gangakhedkar Date: Fri, 15 Jan 2010 21:01:47 +0530 Subject: ALSA: hda - Fix mute led GPIO on HP dv-series notebooks On my laptop (HP dv6-1110ax), there are no OEM strings in SMBIOS of type "HP_Mute_LED*". Hence, the GPIO for the mute button LED doesn't get set properly. I didn't find the strings in my cousin's laptop (HP dv9500t CTO) either. As per the documentation of find_mute_led_gpio(), these strings occur in HP B-series systems - so, before scanning the SMBIOS strings, we need to check if we're dealing with a B-series system. Need to get confirmation from HP if this logic takes care of all the systems. I'm trying to poke a friend there. Signed-off-by: Kunal Gangakhedkar Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 61 +++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 2291a8396817..799ba2570902 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4730,6 +4730,26 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) } } +static int hp_blike_system(u32 subsystem_id); + +static void set_hp_led_gpio(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + switch (codec->vendor_id) { + case 0x111d7608: + /* GPIO 0 */ + spec->gpio_led = 0x01; + break; + case 0x111d7600: + case 0x111d7601: + case 0x111d7602: + case 0x111d7603: + /* GPIO 3 */ + spec->gpio_led = 0x08; + break; + } +} + /* * This method searches for the mute LED GPIO configuration * provided as OEM string in SMBIOS. The format of that string @@ -4741,6 +4761,14 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) * * So, HP B-series like systems may have HP_Mute_LED_0 (current models) * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings + * + * + * The dv-series laptops don't seem to have the HP_Mute_LED* strings in + * SMBIOS - at least the ones I have seen do not have them - which include + * my own system (HP Pavilion dv6-1110ax) and my cousin's + * HP Pavilion dv9500t CTO. + * Need more information on whether it is true across the entire series. + * -- kunal */ static int find_mute_led_gpio(struct hda_codec *codec) { @@ -4751,28 +4779,27 @@ static int find_mute_led_gpio(struct hda_codec *codec) while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { if (sscanf(dev->name, "HP_Mute_LED_%d_%d", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { spec->gpio_led = 1 << spec->gpio_led; return 1; } if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - switch (codec->vendor_id) { - case 0x111d7608: - /* GPIO 0 */ - spec->gpio_led = 0x01; - return 1; - case 0x111d7600: - case 0x111d7601: - case 0x111d7602: - case 0x111d7603: - /* GPIO 3 */ - spec->gpio_led = 0x08; - return 1; - } + &spec->gpio_led_polarity) == 1) { + set_hp_led_gpio(codec); + return 1; } } + + /* + * Fallback case - if we don't find the DMI strings, + * we statically set the GPIO - if not a B-series system. + */ + if (!hp_blike_system(codec->subsystem_id)) { + set_hp_led_gpio(codec); + spec->gpio_led_polarity = 1; + return 1; + } } return 0; } @@ -5548,6 +5575,8 @@ again: spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); + snd_printdd("Found board config: %d\n", spec->board_config); + switch (spec->board_config) { case STAC_HP_M4: /* enable internal microphone */ -- cgit v1.2.2 From eaa9b3a748539651f50e3a234c8854e1b42a839a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 17 Jan 2010 13:09:33 +0100 Subject: ALSA: hda - Fix capture on Sony VAIO with single input Sony VAIO VGN-P11G with ALC262 codec has only one input pin, and the recording doesn't work with model=auto because ALC262 parser sets the wrong cap NIDs to choose the route and the default route for the sole input pin wasn't initialized properly. This patch solves these issues. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 62 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index abae1007cea2..3f92def752fd 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1230,6 +1230,8 @@ static void alc_init_auto_mic(struct hda_codec *codec) return; /* invalid entry */ } } + if (!ext || !fixed) + return; if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) return; /* no unsol support */ snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n", @@ -4812,6 +4814,49 @@ static void fixup_automic_adc(struct hda_codec *codec) spec->auto_mic = 0; /* disable auto-mic to be sure */ } +/* choose the ADC/MUX containing the input pin and initialize the setup */ +static void fixup_single_adc(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + int i; + + /* search for the input pin; there must be only one */ + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (spec->autocfg.input_pins[i]) { + pin = spec->autocfg.input_pins[i]; + break; + } + } + if (!pin) + return; + + /* set the default connection to that pin */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[i] : spec->adc_nids[i]; + int idx; + + idx = get_connection_index(codec, cap, pin); + if (idx < 0) + continue; + /* use only this ADC */ + if (spec->capsrc_nids) + spec->capsrc_nids += i; + spec->adc_nids += i; + spec->num_adc_nids = 1; + /* select or unmute this route */ + if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { + snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, + HDA_AMP_MUTE, 0); + } else { + snd_hda_codec_write_cache(codec, cap, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } + return; + } +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -4824,14 +4869,15 @@ static void set_capture_mixer(struct hda_codec *codec) alc_capture_mixer3 }, }; if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { - int mux; - if (spec->auto_mic) { - mux = 0; + int mux = 0; + if (spec->auto_mic) fixup_automic_adc(codec); - } else if (spec->input_mux && spec->input_mux->num_items > 1) - mux = 1; - else - mux = 0; + else if (spec->input_mux) { + if (spec->input_mux->num_items > 1) + mux = 1; + else if (spec->input_mux->num_items == 1) + fixup_single_adc(codec); + } spec->cap_mixer = caps[mux][spec->num_adc_nids - 1]; } } @@ -11203,7 +11249,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, } #define alc262_auto_create_input_ctls \ - alc880_auto_create_input_ctls + alc882_auto_create_input_ctls /* * generic initialization of ADC, input mixers and output mixers -- cgit v1.2.2 From d1db38c015a392b0ea8c15ab95abb3ee768b8d47 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 18 Jan 2010 15:44:04 +0100 Subject: sound: virtuoso: add Xonar DS support Add experimental support for the Asus Xonar DS. Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela --- sound/pci/Kconfig | 1 + sound/pci/oxygen/Makefile | 2 +- sound/pci/oxygen/virtuoso.c | 3 + sound/pci/oxygen/wm8766.h | 73 +++ sound/pci/oxygen/wm8776.h | 177 +++++++ sound/pci/oxygen/xonar.h | 2 + sound/pci/oxygen/xonar_wm87x6.c | 1021 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 1278 insertions(+), 1 deletion(-) create mode 100644 sound/pci/oxygen/wm8766.h create mode 100644 sound/pci/oxygen/wm8776.h create mode 100644 sound/pci/oxygen/xonar_wm87x6.c (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 351654cf7b09..1298c68d6bf0 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -789,6 +789,7 @@ config SND_VIRTUOSO Say Y here to include support for sound cards based on the Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, Essence ST (Deluxe), and Essence STX. + Support for the DS is experimental. Support for the HDAV1.3 (Deluxe) is very experimental. To compile this driver as a module, choose M here: the module diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile index 389941cf6100..acd8f15f7bff 100644 --- a/sound/pci/oxygen/Makefile +++ b/sound/pci/oxygen/Makefile @@ -2,7 +2,7 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o snd-hifier-objs := hifier.o snd-oxygen-objs := oxygen.o snd-virtuoso-objs := virtuoso.o xonar_lib.o \ - xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o + xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o obj-$(CONFIG_SND_HIFIER) += snd-hifier.o diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 6accaf9580b2..563b6f50821f 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -49,6 +49,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = { { OXYGEN_PCI_SUBID(0x1043, 0x834f) }, { OXYGEN_PCI_SUBID(0x1043, 0x835c) }, { OXYGEN_PCI_SUBID(0x1043, 0x835d) }, + { OXYGEN_PCI_SUBID(0x1043, 0x838e) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, { } }; @@ -61,6 +62,8 @@ static int __devinit get_xonar_model(struct oxygen *chip, return 0; if (get_xonar_cs43xx_model(chip, id) >= 0) return 0; + if (get_xonar_wm87x6_model(chip, id) >= 0) + return 0; return -EINVAL; } diff --git a/sound/pci/oxygen/wm8766.h b/sound/pci/oxygen/wm8766.h new file mode 100644 index 000000000000..e0e849a7eaeb --- /dev/null +++ b/sound/pci/oxygen/wm8766.h @@ -0,0 +1,73 @@ +#ifndef WM8766_H_INCLUDED +#define WM8766_H_INCLUDED + +#define WM8766_LDA1 0x00 +#define WM8766_RDA1 0x01 +#define WM8766_DAC_CTRL 0x02 +#define WM8766_INT_CTRL 0x03 +#define WM8766_LDA2 0x04 +#define WM8766_RDA2 0x05 +#define WM8766_LDA3 0x06 +#define WM8766_RDA3 0x07 +#define WM8766_MASTDA 0x08 +#define WM8766_DAC_CTRL2 0x09 +#define WM8766_DAC_CTRL3 0x0a +#define WM8766_MUTE1 0x0c +#define WM8766_MUTE2 0x0f +#define WM8766_RESET 0x1f + +/* LDAx/RDAx/MASTDA */ +#define WM8766_ATT_MASK 0x0ff +#define WM8766_UPDATE 0x100 +/* DAC_CTRL */ +#define WM8766_MUTEALL 0x001 +#define WM8766_DEEMPALL 0x002 +#define WM8766_PWDN 0x004 +#define WM8766_ATC 0x008 +#define WM8766_IZD 0x010 +#define WM8766_PL_LEFT_MASK 0x060 +#define WM8766_PL_LEFT_MUTE 0x000 +#define WM8766_PL_LEFT_LEFT 0x020 +#define WM8766_PL_LEFT_RIGHT 0x040 +#define WM8766_PL_LEFT_LRMIX 0x060 +#define WM8766_PL_RIGHT_MASK 0x180 +#define WM8766_PL_RIGHT_MUTE 0x000 +#define WM8766_PL_RIGHT_LEFT 0x080 +#define WM8766_PL_RIGHT_RIGHT 0x100 +#define WM8766_PL_RIGHT_LRMIX 0x180 +/* INT_CTRL */ +#define WM8766_FMT_MASK 0x003 +#define WM8766_FMT_RJUST 0x000 +#define WM8766_FMT_LJUST 0x001 +#define WM8766_FMT_I2S 0x002 +#define WM8766_FMT_DSP 0x003 +#define WM8766_LRP 0x004 +#define WM8766_BCP 0x008 +#define WM8766_IWL_MASK 0x030 +#define WM8766_IWL_16 0x000 +#define WM8766_IWL_20 0x010 +#define WM8766_IWL_24 0x020 +#define WM8766_IWL_32 0x030 +#define WM8766_PHASE_MASK 0x1c0 +/* DAC_CTRL2 */ +#define WM8766_ZCD 0x001 +#define WM8766_DZFM_MASK 0x006 +#define WM8766_DMUTE_MASK 0x038 +#define WM8766_DEEMP_MASK 0x1c0 +/* DAC_CTRL3 */ +#define WM8766_DACPD_MASK 0x00e +#define WM8766_PWRDNALL 0x010 +#define WM8766_MS 0x020 +#define WM8766_RATE_MASK 0x1c0 +#define WM8766_RATE_128 0x000 +#define WM8766_RATE_192 0x040 +#define WM8766_RATE_256 0x080 +#define WM8766_RATE_384 0x0c0 +#define WM8766_RATE_512 0x100 +#define WM8766_RATE_768 0x140 +/* MUTE1 */ +#define WM8766_MPD1 0x040 +/* MUTE2 */ +#define WM8766_MPD2 0x020 + +#endif diff --git a/sound/pci/oxygen/wm8776.h b/sound/pci/oxygen/wm8776.h new file mode 100644 index 000000000000..1a96f5615727 --- /dev/null +++ b/sound/pci/oxygen/wm8776.h @@ -0,0 +1,177 @@ +#ifndef WM8776_H_INCLUDED +#define WM8776_H_INCLUDED + +/* + * the following register names are from: + * wm8776.h -- WM8776 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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. + */ + +#define WM8776_HPLVOL 0x00 +#define WM8776_HPRVOL 0x01 +#define WM8776_HPMASTER 0x02 +#define WM8776_DACLVOL 0x03 +#define WM8776_DACRVOL 0x04 +#define WM8776_DACMASTER 0x05 +#define WM8776_PHASESWAP 0x06 +#define WM8776_DACCTRL1 0x07 +#define WM8776_DACMUTE 0x08 +#define WM8776_DACCTRL2 0x09 +#define WM8776_DACIFCTRL 0x0a +#define WM8776_ADCIFCTRL 0x0b +#define WM8776_MSTRCTRL 0x0c +#define WM8776_PWRDOWN 0x0d +#define WM8776_ADCLVOL 0x0e +#define WM8776_ADCRVOL 0x0f +#define WM8776_ALCCTRL1 0x10 +#define WM8776_ALCCTRL2 0x11 +#define WM8776_ALCCTRL3 0x12 +#define WM8776_NOISEGATE 0x13 +#define WM8776_LIMITER 0x14 +#define WM8776_ADCMUX 0x15 +#define WM8776_OUTMUX 0x16 +#define WM8776_RESET 0x17 + + +/* HPLVOL/HPRVOL/HPMASTER */ +#define WM8776_HPATT_MASK 0x07f +#define WM8776_HPZCEN 0x080 +#define WM8776_UPDATE 0x100 + +/* DACLVOL/DACRVOL/DACMASTER */ +#define WM8776_DATT_MASK 0x0ff +/*#define WM8776_UPDATE 0x100*/ + +/* PHASESWAP */ +#define WM8776_PH_MASK 0x003 + +/* DACCTRL1 */ +#define WM8776_DZCEN 0x001 +#define WM8776_ATC 0x002 +#define WM8776_IZD 0x004 +#define WM8776_TOD 0x008 +#define WM8776_PL_LEFT_MASK 0x030 +#define WM8776_PL_LEFT_MUTE 0x000 +#define WM8776_PL_LEFT_LEFT 0x010 +#define WM8776_PL_LEFT_RIGHT 0x020 +#define WM8776_PL_LEFT_LRMIX 0x030 +#define WM8776_PL_RIGHT_MASK 0x0c0 +#define WM8776_PL_RIGHT_MUTE 0x000 +#define WM8776_PL_RIGHT_LEFT 0x040 +#define WM8776_PL_RIGHT_RIGHT 0x080 +#define WM8776_PL_RIGHT_LRMIX 0x0c0 + +/* DACMUTE */ +#define WM8776_DMUTE 0x001 + +/* DACCTRL2 */ +#define WM8776_DEEMPH 0x001 +#define WM8776_DZFM_MASK 0x006 +#define WM8776_DZFM_NONE 0x000 +#define WM8776_DZFM_LR 0x002 +#define WM8776_DZFM_BOTH 0x004 +#define WM8776_DZFM_EITHER 0x006 + +/* DACIFCTRL */ +#define WM8776_DACFMT_MASK 0x003 +#define WM8776_DACFMT_RJUST 0x000 +#define WM8776_DACFMT_LJUST 0x001 +#define WM8776_DACFMT_I2S 0x002 +#define WM8776_DACFMT_DSP 0x003 +#define WM8776_DACLRP 0x004 +#define WM8776_DACBCP 0x008 +#define WM8776_DACWL_MASK 0x030 +#define WM8776_DACWL_16 0x000 +#define WM8776_DACWL_20 0x010 +#define WM8776_DACWL_24 0x020 +#define WM8776_DACWL_32 0x030 + +/* ADCIFCTRL */ +#define WM8776_ADCFMT_MASK 0x003 +#define WM8776_ADCFMT_RJUST 0x000 +#define WM8776_ADCFMT_LJUST 0x001 +#define WM8776_ADCFMT_I2S 0x002 +#define WM8776_ADCFMT_DSP 0x003 +#define WM8776_ADCLRP 0x004 +#define WM8776_ADCBCP 0x008 +#define WM8776_ADCWL_MASK 0x030 +#define WM8776_ADCWL_16 0x000 +#define WM8776_ADCWL_20 0x010 +#define WM8776_ADCWL_24 0x020 +#define WM8776_ADCWL_32 0x030 +#define WM8776_ADCMCLK 0x040 +#define WM8776_ADCHPD 0x100 + +/* MSTRCTRL */ +#define WM8776_ADCRATE_MASK 0x007 +#define WM8776_ADCRATE_256 0x002 +#define WM8776_ADCRATE_384 0x003 +#define WM8776_ADCRATE_512 0x004 +#define WM8776_ADCRATE_768 0x005 +#define WM8776_ADCOSR 0x008 +#define WM8776_DACRATE_MASK 0x070 +#define WM8776_DACRATE_128 0x000 +#define WM8776_DACRATE_192 0x010 +#define WM8776_DACRATE_256 0x020 +#define WM8776_DACRATE_384 0x030 +#define WM8776_DACRATE_512 0x040 +#define WM8776_DACRATE_768 0x050 +#define WM8776_DACMS 0x080 +#define WM8776_ADCMS 0x100 + +/* PWRDOWN */ +#define WM8776_PDWN 0x001 +#define WM8776_ADCPD 0x002 +#define WM8776_DACPD 0x004 +#define WM8776_HPPD 0x008 +#define WM8776_AINPD 0x040 + +/* ADCLVOL/ADCRVOL */ +#define WM8776_AGMASK 0x0ff +#define WM8776_ZCA 0x100 + +/* ALCCTRL1 */ +#define WM8776_LCT_MASK 0x00f +#define WM8776_MAXGAIN_MASK 0x070 +#define WM8776_LCSEL_MASK 0x180 +#define WM8776_LCSEL_LIMITER 0x000 +#define WM8776_LCSEL_ALC_RIGHT 0x080 +#define WM8776_LCSEL_ALC_LEFT 0x100 +#define WM8776_LCSEL_ALC_STEREO 0x180 + +/* ALCCTRL2 */ +#define WM8776_HLD_MASK 0x00f +#define WM8776_ALCZC 0x080 +#define WM8776_LCEN 0x100 + +/* ALCCTRL3 */ +#define WM8776_ATK_MASK 0x00f +#define WM8776_DCY_MASK 0x0f0 + +/* NOISEGATE */ +#define WM8776_NGAT 0x001 +#define WM8776_NGTH_MASK 0x01c + +/* LIMITER */ +#define WM8776_MAXATTEN_MASK 0x00f +#define WM8776_TRANWIN_MASK 0x070 + +/* ADCMUX */ +#define WM8776_AMX_MASK 0x01f +#define WM8776_MUTERA 0x040 +#define WM8776_MUTELA 0x080 +#define WM8776_LRBOTH 0x100 + +/* OUTMUX */ +#define WM8776_MX_DAC 0x001 +#define WM8776_MX_AUX 0x002 +#define WM8776_MX_BYPASS 0x004 + +#endif diff --git a/sound/pci/oxygen/xonar.h b/sound/pci/oxygen/xonar.h index 89b3ed814d64..b35343b0a9a5 100644 --- a/sound/pci/oxygen/xonar.h +++ b/sound/pci/oxygen/xonar.h @@ -35,6 +35,8 @@ int get_xonar_pcm179x_model(struct oxygen *chip, const struct pci_device_id *id); int get_xonar_cs43xx_model(struct oxygen *chip, const struct pci_device_id *id); +int get_xonar_wm87x6_model(struct oxygen *chip, + const struct pci_device_id *id); /* HDMI helper functions */ diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c new file mode 100644 index 000000000000..7754db166d9e --- /dev/null +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -0,0 +1,1021 @@ +/* + * card driver for models with WM8776/WM8766 DACs (Xonar DS) + * + * Copyright (c) Clemens Ladisch + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see . + */ + +/* + * Xonar DS + * -------- + * + * CMI8788: + * + * SPI 0 -> WM8766 (surround, center/LFE, back) + * SPI 1 -> WM8776 (front, input) + * + * GPIO 4 <- headphone detect + * GPIO 6 -> route input jack to input 1/2 (1/0) + * GPIO 7 -> enable output to speakers + * GPIO 8 -> enable output to speakers + */ + +#include +#include +#include +#include +#include +#include +#include +#include "xonar.h" +#include "wm8776.h" +#include "wm8766.h" + +#define GPIO_DS_HP_DETECT 0x0010 +#define GPIO_DS_INPUT_ROUTE 0x0040 +#define GPIO_DS_OUTPUT_ENABLE 0x0180 + +#define LC_CONTROL_LIMITER 0x40000000 +#define LC_CONTROL_ALC 0x20000000 + +struct xonar_wm87x6 { + struct xonar_generic generic; + u16 wm8776_regs[0x17]; + u16 wm8766_regs[0x10]; + struct snd_kcontrol *lc_controls[13]; +}; + +static void wm8776_write(struct oxygen *chip, + unsigned int reg, unsigned int value) +{ + struct xonar_wm87x6 *data = chip->model_data; + + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (1 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_LO, + (reg << 9) | value); + if (reg < ARRAY_SIZE(data->wm8776_regs)) { + if (reg >= WM8776_HPLVOL || reg <= WM8776_DACMASTER) + value &= ~WM8776_UPDATE; + data->wm8776_regs[reg] = value; + } +} + +static void wm8776_write_cached(struct oxygen *chip, + unsigned int reg, unsigned int value) +{ + struct xonar_wm87x6 *data = chip->model_data; + + if (reg >= ARRAY_SIZE(data->wm8776_regs) || + value != data->wm8776_regs[reg]) + wm8776_write(chip, reg, value); +} + +static void wm8766_write(struct oxygen *chip, + unsigned int reg, unsigned int value) +{ + struct xonar_wm87x6 *data = chip->model_data; + + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (0 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_LO, + (reg << 9) | value); + if (reg < ARRAY_SIZE(data->wm8766_regs)) + data->wm8766_regs[reg] = value; +} + +static void wm8766_write_cached(struct oxygen *chip, + unsigned int reg, unsigned int value) +{ + struct xonar_wm87x6 *data = chip->model_data; + + if (reg >= ARRAY_SIZE(data->wm8766_regs) || + value != data->wm8766_regs[reg]) { + if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) || + (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA)) + value &= ~WM8766_UPDATE; + wm8766_write(chip, reg, value); + } +} + +static void wm8776_registers_init(struct oxygen *chip) +{ + struct xonar_wm87x6 *data = chip->model_data; + + wm8776_write(chip, WM8776_RESET, 0); + wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN | + WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT); + wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0); + wm8776_write(chip, WM8776_DACIFCTRL, + WM8776_DACFMT_LJUST | WM8776_DACWL_24); + wm8776_write(chip, WM8776_ADCIFCTRL, + data->wm8776_regs[WM8776_ADCIFCTRL]); + wm8776_write(chip, WM8776_MSTRCTRL, data->wm8776_regs[WM8776_MSTRCTRL]); + wm8776_write(chip, WM8776_PWRDOWN, data->wm8776_regs[WM8776_PWRDOWN]); + wm8776_write(chip, WM8776_HPLVOL, data->wm8776_regs[WM8776_HPLVOL]); + wm8776_write(chip, WM8776_HPRVOL, data->wm8776_regs[WM8776_HPRVOL] | + WM8776_UPDATE); + wm8776_write(chip, WM8776_ADCLVOL, data->wm8776_regs[WM8776_ADCLVOL]); + wm8776_write(chip, WM8776_ADCRVOL, data->wm8776_regs[WM8776_ADCRVOL]); + wm8776_write(chip, WM8776_ADCMUX, data->wm8776_regs[WM8776_ADCMUX]); + wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0]); + wm8776_write(chip, WM8776_DACRVOL, chip->dac_volume[1] | WM8776_UPDATE); +} + +static void wm8766_registers_init(struct oxygen *chip) +{ + wm8766_write(chip, WM8766_RESET, 0); + wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24); + wm8766_write(chip, WM8766_DAC_CTRL2, + WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); + wm8766_write(chip, WM8766_LDA1, chip->dac_volume[2]); + wm8766_write(chip, WM8766_RDA1, chip->dac_volume[3]); + wm8766_write(chip, WM8766_LDA2, chip->dac_volume[4]); + wm8766_write(chip, WM8766_RDA2, chip->dac_volume[5]); + wm8766_write(chip, WM8766_LDA3, chip->dac_volume[6]); + wm8766_write(chip, WM8766_RDA3, chip->dac_volume[7] | WM8766_UPDATE); +} + +static void wm8776_init(struct oxygen *chip) +{ + struct xonar_wm87x6 *data = chip->model_data; + + data->wm8776_regs[WM8776_HPLVOL] = (0x79 - 60) | WM8776_HPZCEN; + data->wm8776_regs[WM8776_HPRVOL] = (0x79 - 60) | WM8776_HPZCEN; + data->wm8776_regs[WM8776_ADCIFCTRL] = + WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK; + data->wm8776_regs[WM8776_MSTRCTRL] = + WM8776_ADCRATE_256 | WM8776_DACRATE_256; + data->wm8776_regs[WM8776_PWRDOWN] = WM8776_HPPD; + data->wm8776_regs[WM8776_ADCLVOL] = 0xa5 | WM8776_ZCA; + data->wm8776_regs[WM8776_ADCRVOL] = 0xa5 | WM8776_ZCA; + data->wm8776_regs[WM8776_ADCMUX] = 0x001; + wm8776_registers_init(chip); +} + +static void xonar_ds_init(struct oxygen *chip) +{ + struct xonar_wm87x6 *data = chip->model_data; + + data->generic.anti_pop_delay = 300; + data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE; + + wm8776_init(chip); + wm8766_registers_init(chip); + + oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE, + GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE); + oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + + xonar_enable_output(chip); + + snd_component_add(chip->card, "WM8776"); + snd_component_add(chip->card, "WM8766"); +} + +static void xonar_ds_cleanup(struct oxygen *chip) +{ + xonar_disable_output(chip); +} + +static void xonar_ds_suspend(struct oxygen *chip) +{ + xonar_ds_cleanup(chip); +} + +static void xonar_ds_resume(struct oxygen *chip) +{ + wm8776_registers_init(chip); + wm8766_registers_init(chip); + xonar_enable_output(chip); +} + +static void wm8776_adc_hardware_filter(unsigned int channel, + struct snd_pcm_hardware *hardware) +{ + if (channel == PCM_A) { + hardware->rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + hardware->rate_max = 96000; + } +} + +static void set_wm87x6_dac_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ +} + +static void set_wm8776_adc_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + u16 reg; + + reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256; + if (params_rate(params) > 48000) + reg |= WM8776_ADCOSR; + wm8776_write_cached(chip, WM8776_MSTRCTRL, reg); +} + +static void update_wm8776_volume(struct oxygen *chip) +{ + struct xonar_wm87x6 *data = chip->model_data; + u8 to_change; + + if (chip->dac_volume[0] == chip->dac_volume[1]) { + if (chip->dac_volume[0] != data->wm8776_regs[WM8776_DACLVOL] || + chip->dac_volume[1] != data->wm8776_regs[WM8776_DACRVOL]) { + wm8776_write(chip, WM8776_DACMASTER, + chip->dac_volume[0] | WM8776_UPDATE); + data->wm8776_regs[WM8776_DACLVOL] = chip->dac_volume[0]; + data->wm8776_regs[WM8776_DACRVOL] = chip->dac_volume[0]; + } + } else { + to_change = (chip->dac_volume[0] != + data->wm8776_regs[WM8776_DACLVOL]) << 0; + to_change |= (chip->dac_volume[1] != + data->wm8776_regs[WM8776_DACLVOL]) << 1; + if (to_change & 1) + wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0] | + ((to_change & 2) ? 0 : WM8776_UPDATE)); + if (to_change & 2) + wm8776_write(chip, WM8776_DACRVOL, + chip->dac_volume[1] | WM8776_UPDATE); + } +} + +static void update_wm87x6_volume(struct oxygen *chip) +{ + static const u8 wm8766_regs[6] = { + WM8766_LDA1, WM8766_RDA1, + WM8766_LDA2, WM8766_RDA2, + WM8766_LDA3, WM8766_RDA3, + }; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int i; + u8 to_change; + + update_wm8776_volume(chip); + if (chip->dac_volume[2] == chip->dac_volume[3] && + chip->dac_volume[2] == chip->dac_volume[4] && + chip->dac_volume[2] == chip->dac_volume[5] && + chip->dac_volume[2] == chip->dac_volume[6] && + chip->dac_volume[2] == chip->dac_volume[7]) { + to_change = 0; + for (i = 0; i < 6; ++i) + if (chip->dac_volume[2] != + data->wm8766_regs[wm8766_regs[i]]) + to_change = 1; + if (to_change) { + wm8766_write(chip, WM8766_MASTDA, + chip->dac_volume[2] | WM8766_UPDATE); + for (i = 0; i < 6; ++i) + data->wm8766_regs[wm8766_regs[i]] = + chip->dac_volume[2]; + } + } else { + to_change = 0; + for (i = 0; i < 6; ++i) + to_change |= (chip->dac_volume[2 + i] != + data->wm8766_regs[wm8766_regs[i]]) << i; + for (i = 0; i < 6; ++i) + if (to_change & (1 << i)) + wm8766_write(chip, wm8766_regs[i], + chip->dac_volume[2 + i] | + ((to_change & (0x3e << i)) + ? 0 : WM8766_UPDATE)); + } +} + +static void update_wm8776_mute(struct oxygen *chip) +{ + wm8776_write_cached(chip, WM8776_DACMUTE, + chip->dac_mute ? WM8776_DMUTE : 0); +} + +static void update_wm87x6_mute(struct oxygen *chip) +{ + update_wm8776_mute(chip); + wm8766_write_cached(chip, WM8766_DAC_CTRL2, WM8766_ZCD | + (chip->dac_mute ? WM8766_DMUTE_MASK : 0)); +} + +static void xonar_ds_gpio_changed(struct oxygen *chip) +{ + u16 bits; + + bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); + snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT)); +} + +static int wm8776_bit_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + u16 bit = ctl->private_value & 0xffff; + unsigned int reg_index = (ctl->private_value >> 16) & 0xff; + bool invert = (ctl->private_value >> 24) & 1; + + value->value.integer.value[0] = + ((data->wm8776_regs[reg_index] & bit) != 0) ^ invert; + return 0; +} + +static int wm8776_bit_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + u16 bit = ctl->private_value & 0xffff; + u16 reg_value; + unsigned int reg_index = (ctl->private_value >> 16) & 0xff; + bool invert = (ctl->private_value >> 24) & 1; + int changed; + + mutex_lock(&chip->mutex); + reg_value = data->wm8776_regs[reg_index] & ~bit; + if (value->value.integer.value[0] ^ invert) + reg_value |= bit; + changed = reg_value != data->wm8776_regs[reg_index]; + if (changed) + wm8776_write(chip, reg_index, reg_value); + mutex_unlock(&chip->mutex); + return changed; +} + +static int wm8776_field_enum_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const hld[16] = { + "0 ms", "2.67 ms", "5.33 ms", "10.6 ms", + "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", + "341 ms", "683 ms", "1.37 s", "2.73 s", + "5.46 s", "10.9 s", "21.8 s", "43.7 s", + }; + static const char *const atk_lim[11] = { + "0.25 ms", "0.5 ms", "1 ms", "2 ms", + "4 ms", "8 ms", "16 ms", "32 ms", + "64 ms", "128 ms", "256 ms", + }; + static const char *const atk_alc[11] = { + "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms", + "134 ms", "269 ms", "538 ms", "1.08 s", + "2.15 s", "4.3 s", "8.6 s", + }; + static const char *const dcy_lim[11] = { + "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms", + "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", + "307 ms", "614 ms", "1.23 s", + }; + static const char *const dcy_alc[11] = { + "33.5 ms", "67.0 ms", "134 ms", "268 ms", + "536 ms", "1.07 s", "2.14 s", "4.29 s", + "8.58 s", "17.2 s", "34.3 s", + }; + static const char *const tranwin[8] = { + "0 us", "62.5 us", "125 us", "250 us", + "500 us", "1 ms", "2 ms", "4 ms", + }; + u8 max; + const char *const *names; + + max = (ctl->private_value >> 12) & 0xf; + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = max + 1; + if (info->value.enumerated.item > max) + info->value.enumerated.item = max; + switch ((ctl->private_value >> 24) & 0x1f) { + case WM8776_ALCCTRL2: + names = hld; + break; + case WM8776_ALCCTRL3: + if (((ctl->private_value >> 20) & 0xf) == 0) { + if (ctl->private_value & LC_CONTROL_LIMITER) + names = atk_lim; + else + names = atk_alc; + } else { + if (ctl->private_value & LC_CONTROL_LIMITER) + names = dcy_lim; + else + names = dcy_alc; + } + break; + case WM8776_LIMITER: + names = tranwin; + break; + default: + return -ENXIO; + } + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int wm8776_field_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 = (ctl->private_value >> 8) & 0xf; + info->value.integer.max = (ctl->private_value >> 12) & 0xf; + return 0; +} + +static void wm8776_field_set_from_ctl(struct snd_kcontrol *ctl) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int value, reg_index, mode; + u8 min, max, shift; + u16 mask, reg_value; + bool invert; + + if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) == + WM8776_LCSEL_LIMITER) + mode = LC_CONTROL_LIMITER; + else + mode = LC_CONTROL_ALC; + if (!(ctl->private_value & mode)) + return; + + value = ctl->private_value & 0xf; + min = (ctl->private_value >> 8) & 0xf; + max = (ctl->private_value >> 12) & 0xf; + mask = (ctl->private_value >> 16) & 0xf; + shift = (ctl->private_value >> 20) & 0xf; + reg_index = (ctl->private_value >> 24) & 0x1f; + invert = (ctl->private_value >> 29) & 0x1; + + if (invert) + value = max - (value - min); + reg_value = data->wm8776_regs[reg_index]; + reg_value &= ~(mask << shift); + reg_value |= value << shift; + wm8776_write_cached(chip, reg_index, reg_value); +} + +static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value) +{ + struct oxygen *chip = ctl->private_data; + u8 min, max; + int changed; + + min = (ctl->private_value >> 8) & 0xf; + max = (ctl->private_value >> 12) & 0xf; + if (value < min || value > max) + return -EINVAL; + mutex_lock(&chip->mutex); + changed = value != (ctl->private_value & 0xf); + if (changed) { + ctl->private_value = (ctl->private_value & ~0xf) | value; + wm8776_field_set_from_ctl(ctl); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int wm8776_field_enum_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.enumerated.item[0] = ctl->private_value & 0xf; + return 0; +} + +static int wm8776_field_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.integer.value[0] = ctl->private_value & 0xf; + return 0; +} + +static int wm8776_field_enum_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + return wm8776_field_set(ctl, value->value.enumerated.item[0]); +} + +static int wm8776_field_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + return wm8776_field_set(ctl, value->value.integer.value[0]); +} + +static int wm8776_hp_vol_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0x79 - 60; + info->value.integer.max = 0x7f; + return 0; +} + +static int wm8776_hp_vol_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = + data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK; + value->value.integer.value[1] = + data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK; + mutex_unlock(&chip->mutex); + return 0; +} + +static int wm8776_hp_vol_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + u8 to_update; + + mutex_lock(&chip->mutex); + to_update = (value->value.integer.value[0] != + (data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK)) + << 0; + to_update |= (value->value.integer.value[1] != + (data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK)) + << 1; + if (value->value.integer.value[0] == value->value.integer.value[1]) { + if (to_update) { + wm8776_write(chip, WM8776_HPMASTER, + value->value.integer.value[0] | + WM8776_HPZCEN | WM8776_UPDATE); + data->wm8776_regs[WM8776_HPLVOL] = + value->value.integer.value[0] | WM8776_HPZCEN; + data->wm8776_regs[WM8776_HPRVOL] = + value->value.integer.value[0] | WM8776_HPZCEN; + } + } else { + if (to_update & 1) + wm8776_write(chip, WM8776_HPLVOL, + value->value.integer.value[0] | + WM8776_HPZCEN | + ((to_update & 2) ? 0 : WM8776_UPDATE)); + if (to_update & 2) + wm8776_write(chip, WM8776_HPRVOL, + value->value.integer.value[1] | + WM8776_HPZCEN | WM8776_UPDATE); + } + mutex_unlock(&chip->mutex); + return to_update != 0; +} + +static int wm8776_input_mux_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int mux_bit = ctl->private_value; + + value->value.integer.value[0] = + !!(data->wm8776_regs[WM8776_ADCMUX] & mux_bit); + return 0; +} + +static int wm8776_input_mux_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int mux_bit = ctl->private_value; + u16 reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->wm8776_regs[WM8776_ADCMUX]; + if (value->value.integer.value[0]) { + reg &= ~0x003; + reg |= mux_bit; + } else + reg &= ~mux_bit; + changed = reg != data->wm8776_regs[WM8776_ADCMUX]; + if (changed) { + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + reg & 1 ? GPIO_DS_INPUT_ROUTE : 0, + GPIO_DS_INPUT_ROUTE); + wm8776_write(chip, WM8776_ADCMUX, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int wm8776_input_vol_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0xa5; + info->value.integer.max = 0xff; + return 0; +} + +static int wm8776_input_vol_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = + data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK; + value->value.integer.value[1] = + data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK; + mutex_unlock(&chip->mutex); + return 0; +} + +static int wm8776_input_vol_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + int changed = 0; + + mutex_lock(&chip->mutex); + changed = (value->value.integer.value[0] != + (data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) || + (value->value.integer.value[1] != + (data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK)); + wm8776_write_cached(chip, WM8776_ADCLVOL, + value->value.integer.value[0] | WM8776_ZCA); + wm8776_write_cached(chip, WM8776_ADCRVOL, + value->value.integer.value[1] | WM8776_ZCA); + mutex_unlock(&chip->mutex); + return changed; +} + +static int wm8776_level_control_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "None", "Peak Limiter", "Automatic Level Control" + }; + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item >= 3) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int wm8776_level_control_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + + if (!(data->wm8776_regs[WM8776_ALCCTRL2] & WM8776_LCEN)) + value->value.enumerated.item[0] = 0; + else if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) == + WM8776_LCSEL_LIMITER) + value->value.enumerated.item[0] = 1; + else + value->value.enumerated.item[0] = 2; + return 0; +} + +static void activate_control(struct oxygen *chip, + struct snd_kcontrol *ctl, unsigned int mode) +{ + unsigned int access; + + if (ctl->private_value & mode) + access = 0; + else + access = SNDRV_CTL_ELEM_ACCESS_INACTIVE; + if ((ctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != access) { + ctl->vd[0].access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + } +} + +static int wm8776_level_control_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int mode = 0, i; + u16 ctrl1, ctrl2; + int changed; + + if (value->value.enumerated.item[0] >= 3) + return -EINVAL; + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != ctl->private_value; + if (changed) { + ctl->private_value = value->value.enumerated.item[0]; + ctrl1 = data->wm8776_regs[WM8776_ALCCTRL1]; + ctrl2 = data->wm8776_regs[WM8776_ALCCTRL2]; + switch (value->value.enumerated.item[0]) { + default: + wm8776_write_cached(chip, WM8776_ALCCTRL2, + ctrl2 & ~WM8776_LCEN); + break; + case 1: + wm8776_write_cached(chip, WM8776_ALCCTRL1, + (ctrl1 & ~WM8776_LCSEL_MASK) | + WM8776_LCSEL_LIMITER); + wm8776_write_cached(chip, WM8776_ALCCTRL2, + ctrl2 | WM8776_LCEN); + mode = LC_CONTROL_LIMITER; + break; + case 2: + wm8776_write_cached(chip, WM8776_ALCCTRL1, + (ctrl1 & ~WM8776_LCSEL_MASK) | + WM8776_LCSEL_ALC_STEREO); + wm8776_write_cached(chip, WM8776_ALCCTRL2, + ctrl2 | WM8776_LCEN); + mode = LC_CONTROL_ALC; + break; + } + for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i) + activate_control(chip, data->lc_controls[i], mode); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[2] = { + "None", "High-pass Filter" + }; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 2; + if (info->value.enumerated.item >= 2) + info->value.enumerated.item = 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + + value->value.enumerated.item[0] = + !(data->wm8776_regs[WM8776_ADCIFCTRL] & WM8776_ADCHPD); + return 0; +} + +static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_wm87x6 *data = chip->model_data; + unsigned int reg; + int changed; + + mutex_lock(&chip->mutex); + reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD; + if (!value->value.enumerated.item[0]) + reg |= WM8776_ADCHPD; + changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL]; + if (changed) + wm8776_write(chip, WM8776_ADCIFCTRL, reg); + mutex_unlock(&chip->mutex); + return changed; +} + +#define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = wm8776_bit_switch_get, \ + .put = wm8776_bit_switch_put, \ + .private_value = ((reg) << 16) | (bit) | ((invert) << 24) | (flags), \ +} +#define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = (initval) | ((min) << 8) | ((max) << 12) | \ + ((mask) << 16) | ((shift) << 20) | ((reg) << 24) | (flags) +#define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\ + _WM8776_FIELD_CTL(xname " Capture Enum", \ + reg, shift, init, min, max, mask, flags), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_INACTIVE, \ + .info = wm8776_field_enum_info, \ + .get = wm8776_field_enum_get, \ + .put = wm8776_field_enum_put, \ +} +#define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \ + _WM8776_FIELD_CTL(a " Capture Volume", b, c, d, e, f, g, h), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_INACTIVE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = wm8776_field_volume_info, \ + .get = wm8776_field_volume_get, \ + .put = wm8776_field_volume_put, \ + .tlv = { .p = tlv_p }, \ +} + +static const DECLARE_TLV_DB_SCALE(wm87x6_dac_db_scale, -6000, 50, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_adc_db_scale, -2100, 50, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_hp_db_scale, -6000, 100, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_lct_db_scale, -1600, 100, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_db_scale, 0, 400, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_ngth_db_scale, -7800, 600, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_db_scale, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_db_scale, -2100, 400, 0); + +static const struct snd_kcontrol_new ds_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Playback Volume", + .info = wm8776_hp_vol_info, + .get = wm8776_hp_vol_get, + .put = wm8776_hp_vol_put, + .tlv = { .p = wm8776_hp_db_scale }, + }, + WM8776_BIT_SWITCH("Headphone Playback Switch", + WM8776_PWRDOWN, WM8776_HPPD, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Capture Volume", + .info = wm8776_input_vol_info, + .get = wm8776_input_vol_get, + .put = wm8776_input_vol_put, + .tlv = { .p = wm8776_adc_db_scale }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Switch", + .info = snd_ctl_boolean_mono_info, + .get = wm8776_input_mux_get, + .put = wm8776_input_mux_put, + .private_value = 1 << 0, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Switch", + .info = snd_ctl_boolean_mono_info, + .get = wm8776_input_mux_get, + .put = wm8776_input_mux_put, + .private_value = 1 << 1, + }, + WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "ADC Filter Capture Enum", + .info = hpf_info, + .get = hpf_get, + .put = hpf_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Level Control Capture Enum", + .info = wm8776_level_control_info, + .get = wm8776_level_control_get, + .put = wm8776_level_control_put, + .private_value = 0, + }, +}; +static const struct snd_kcontrol_new lc_controls[] = { + WM8776_FIELD_CTL_VOLUME("Limiter Threshold", + WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, + LC_CONTROL_LIMITER, wm8776_lct_db_scale), + WM8776_FIELD_CTL_ENUM("Limiter Attack Time", + WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf, + LC_CONTROL_LIMITER), + WM8776_FIELD_CTL_ENUM("Limiter Decay Time", + WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf, + LC_CONTROL_LIMITER), + WM8776_FIELD_CTL_ENUM("Limiter Transient Window", + WM8776_LIMITER, 4, 2, 0, 7, 0x7, + LC_CONTROL_LIMITER), + WM8776_FIELD_CTL_VOLUME("Limiter Maximum Attenuation", + WM8776_LIMITER, 0, 6, 3, 12, 0xf, + LC_CONTROL_LIMITER, + wm8776_maxatten_lim_db_scale), + WM8776_FIELD_CTL_VOLUME("ALC Target Level", + WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf, + LC_CONTROL_ALC, wm8776_lct_db_scale), + WM8776_FIELD_CTL_ENUM("ALC Attack Time", + WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf, + LC_CONTROL_ALC), + WM8776_FIELD_CTL_ENUM("ALC Decay Time", + WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf, + LC_CONTROL_ALC), + WM8776_FIELD_CTL_VOLUME("ALC Maximum Gain", + WM8776_ALCCTRL1, 4, 7, 1, 7, 0x7, + LC_CONTROL_ALC, wm8776_maxgain_db_scale), + WM8776_FIELD_CTL_VOLUME("ALC Maximum Attenuation", + WM8776_LIMITER, 0, 10, 10, 15, 0xf, + LC_CONTROL_ALC, wm8776_maxatten_alc_db_scale), + WM8776_FIELD_CTL_ENUM("ALC Hold Time", + WM8776_ALCCTRL2, 0, 0, 0, 15, 0xf, + LC_CONTROL_ALC), + WM8776_BIT_SWITCH("Noise Gate Capture Switch", + WM8776_NOISEGATE, WM8776_NGAT, 0, + LC_CONTROL_ALC), + WM8776_FIELD_CTL_VOLUME("Noise Gate Threshold", + WM8776_NOISEGATE, 2, 0, 0, 7, 0x7, + LC_CONTROL_ALC, wm8776_ngth_db_scale), +}; + +static int xonar_ds_control_filter(struct snd_kcontrol_new *template) +{ + if (!strncmp(template->name, "CD Capture ", 11)) + return 1; /* no CD input */ + return 0; +} + +static int xonar_ds_mixer_init(struct oxygen *chip) +{ + struct xonar_wm87x6 *data = chip->model_data; + unsigned int i; + struct snd_kcontrol *ctl; + int err; + + for (i = 0; i < ARRAY_SIZE(ds_controls); ++i) { + ctl = snd_ctl_new1(&ds_controls[i], chip); + if (!ctl) + return -ENOMEM; + err = snd_ctl_add(chip->card, ctl); + if (err < 0) + return err; + } + BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls)); + for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) { + ctl = snd_ctl_new1(&lc_controls[i], chip); + if (!ctl) + return -ENOMEM; + err = snd_ctl_add(chip->card, ctl); + if (err < 0) + return err; + data->lc_controls[i] = ctl; + } + return 0; +} + +static const struct oxygen_model model_xonar_ds = { + .shortname = "Xonar DS", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_ds_init, + .control_filter = xonar_ds_control_filter, + .mixer_init = xonar_ds_mixer_init, + .cleanup = xonar_ds_cleanup, + .suspend = xonar_ds_suspend, + .resume = xonar_ds_resume, + .pcm_hardware_filter = wm8776_adc_hardware_filter, + .get_i2s_mclk = oxygen_default_i2s_mclk, + .set_dac_params = set_wm87x6_dac_params, + .set_adc_params = set_wm8776_adc_params, + .update_dac_volume = update_wm87x6_volume, + .update_dac_mute = update_wm87x6_mute, + .gpio_changed = xonar_ds_gpio_changed, + .dac_tlv = wm87x6_dac_db_scale, + .model_data_size = sizeof(struct xonar_wm87x6), + .device_config = PLAYBACK_0_TO_I2S | + PLAYBACK_1_TO_SPDIF | + CAPTURE_0_FROM_I2S_1, + .dac_channels = 8, + .dac_volume_min = 255 - 2*60, + .dac_volume_max = 255, + .function_flags = OXYGEN_FUNCTION_SPI, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +int __devinit get_xonar_wm87x6_model(struct oxygen *chip, + const struct pci_device_id *id) +{ + switch (id->subdevice) { + case 0x838e: + chip->model = model_xonar_ds; + break; + default: + return -EINVAL; + } + return 0; +} -- cgit v1.2.2 From 4feabefe53eb3742f0b2773a43200d1686f3a288 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Jan 2010 15:38:44 +0100 Subject: ALSA: hda - Fix parsing pin node 0x21 on ALC259 ALC259 has a widget NID 0x21 for the output pin, but it wasn't handled properly in alc268_new_analog_output(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3f92def752fd..79cdae324c5e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12541,6 +12541,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, dac = 0x02; break; case 0x15: + case 0x21: dac = 0x03; break; default: -- cgit v1.2.2 From 3fb4a508b8e7957aa899f32cd6d9d462e102c7ca Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Jan 2010 15:46:37 +0100 Subject: ALSA: hda - Turn on EAPD only if available for Realtek codecs Some codecs disable widgets used for output pins and reserve as vendor- spec widgets. Thus we need to check the widget type and pin cap before actually sending SET_EAPD verbs in the auto-configuration mode. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 79cdae324c5e..6ae610c0111e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1093,6 +1093,16 @@ static void alc889_coef_init(struct hda_codec *codec) snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010); } +/* turn on/off EAPD control (only if available) */ +static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return; + if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + on ? 2 : 0); +} + static void alc_auto_init_amp(struct hda_codec *codec, int type) { unsigned int tmp; @@ -1110,25 +1120,22 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case ALC_INIT_DEFAULT: switch (codec->vendor_id) { case 0x10ec0260: - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_EAPD_BTLENABLE, 2); - snd_hda_codec_write(codec, 0x10, 0, - AC_VERB_SET_EAPD_BTLENABLE, 2); + set_eapd(codec, 0x0f, 1); + set_eapd(codec, 0x10, 1); break; case 0x10ec0262: case 0x10ec0267: case 0x10ec0268: case 0x10ec0269: + case 0x10ec0270: case 0x10ec0272: case 0x10ec0660: case 0x10ec0662: case 0x10ec0663: case 0x10ec0862: case 0x10ec0889: - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_EAPD_BTLENABLE, 2); - snd_hda_codec_write(codec, 0x15, 0, - AC_VERB_SET_EAPD_BTLENABLE, 2); + set_eapd(codec, 0x14, 1); + set_eapd(codec, 0x15, 1); break; } switch (codec->vendor_id) { @@ -1836,10 +1843,8 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE static void alc889_power_eapd(struct hda_codec *codec, int power) { - snd_hda_codec_write(codec, 0x14, 0, - AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); - snd_hda_codec_write(codec, 0x15, 0, - AC_VERB_SET_EAPD_BTLENABLE, power ? 2 : 0); + set_eapd(codec, 0x14, power); + set_eapd(codec, 0x15, power); } #endif -- cgit v1.2.2 From dc99be47667c56046555e89e62f1ac17fa06329a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 20 Jan 2010 08:35:06 +0100 Subject: ALSA: hda - Fix HP T5735 automute This patch fixes the aut-mute setup on HP T5735 with ALC262 codec. Instead of wrong amp, use pin control toggling for muting the speaker now. Tested-by: Lee Trager Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6ae610c0111e..d00e6d1da085 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10382,7 +10382,7 @@ static void alc262_hp_t5735_setup(struct hda_codec *codec) struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; - spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */ + spec->autocfg.speaker_pins[0] = 0x14; } static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { @@ -11793,9 +11793,9 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, - .unsol_event = alc_automute_amp_unsol_event, + .unsol_event = alc_sku_unsol_event, .setup = alc262_hp_t5735_setup, - .init_hook = alc_automute_amp, + .init_hook = alc_inithook, }, [ALC262_HP_RP5700] = { .mixers = { alc262_hp_rp5700_mixer }, -- cgit v1.2.2 From fd0b092a7b14559e2ff17ef3aaefb5d8adc7e15f Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 21 Jan 2010 14:54:38 +0100 Subject: ALSA: hda - AD1988 codec - fix SPDIF-input mixer initialization (unmute) The SPDIF-input pin 0x1c is muted by default in hardware. Unmute appropriate pin to get captured samples instead zeros. Tested on Lenovo Thinkstation. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_analog.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index cecd3c108990..865715e3f938 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2458,6 +2458,12 @@ static struct hda_verb ad1988_spdif_init_verbs[] = { { } }; +static struct hda_verb ad1988_spdif_in_init_verbs[] = { + /* unmute SPDIF input pin */ + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { } +}; + /* AD1989 has no ADC -> SPDIF route */ static struct hda_verb ad1989_spdif_init_verbs[] = { /* SPDIF-1 out pin */ @@ -3193,8 +3199,11 @@ static int patch_ad1988(struct hda_codec *codec) ad1988_spdif_init_verbs; } } - if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) + if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) { spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1988_spdif_in_init_verbs; + } codec->patch_ops = ad198x_patch_ops; switch (board_config) { -- cgit v1.2.2 From 5f6c3de6a79820de124fa2bb1b77d43a09410e42 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 23 Jan 2010 22:19:29 +0100 Subject: ALSA: hda - Minor fixes for Compaq Presario F700 quirk Minor fixes for HP Compaq Presario F700 quirks with Cxt5051 codec: - changed the capture mixer elements to the standard name. - fixed the quirk name string without a space - sorted the quirk list - updated the documentation Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 685015a53292..084600e40829 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1742,8 +1742,8 @@ static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { }; static struct snd_kcontrol_new cxt5051_f700_mixers[] = { - HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1901,17 +1901,17 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_HP] = "hp", [CXT5051_HP_DV6736] = "hp-dv6736", [CXT5051_LENOVO_X200] = "lenovo-x200", - [CXT5051_F700] = "hp 700" + [CXT5051_F700] = "hp-700", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), + SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), - SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), {} }; -- cgit v1.2.2 From 4e4ac60030293cb3d1e4bacf7c8be9aebdb8df61 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 23 Jan 2010 22:29:54 +0100 Subject: ALSA: hda - Fix HP dv6736 capture mixer name Use the standard "Capture" mixer name for HP dv6736 with Cxt5051 codec. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 084600e40829..08c5b32dcd63 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1726,8 +1726,8 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = { }; static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { - HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, -- cgit v1.2.2 From faddaa5d1c0cd29629c9c7e7a9d41ecb3149a064 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 23 Jan 2010 22:31:36 +0100 Subject: ALSA: hda - Add support for Toshiba Satellite M300 Added the support for Toshiba Satellite M300 with Conexant 5051 codec. Since the laptop has no port C connection and the pin reports always the jack sense true, we need to ignore port-C unsol event. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 08c5b32dcd63..56dda9c7f899 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -46,6 +46,8 @@ #define CXT5051_PORTB_EVENT 0x38 #define CXT5051_PORTC_EVENT 0x39 +#define AUTO_MIC_PORTB (1 << 1) +#define AUTO_MIC_PORTC (1 << 2) struct conexant_jack { @@ -74,7 +76,7 @@ struct conexant_spec { */ unsigned int cur_eapd; unsigned int hp_present; - unsigned int no_auto_mic; + unsigned int auto_mic; unsigned int need_dac_fix; /* capture */ @@ -1626,7 +1628,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; unsigned int present; - if (spec->no_auto_mic) + if (!(spec->auto_mic & AUTO_MIC_PORTB)) return; present = snd_hda_jack_detect(codec, 0x17); snd_hda_codec_write(codec, 0x14, 0, @@ -1641,7 +1643,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec) unsigned int present; hda_nid_t new_adc; - if (spec->no_auto_mic) + if (!(spec->auto_mic & AUTO_MIC_PORTC)) return; present = snd_hda_jack_detect(codec, 0x18); if (present) @@ -1757,6 +1759,24 @@ static struct snd_kcontrol_new cxt5051_f700_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + static struct hda_verb cxt5051_init_verbs[] = { /* Line in, Mic */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, @@ -1893,6 +1913,7 @@ enum { CXT5051_HP_DV6736, /* HP without mic switch */ CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ CXT5051_F700, /* HP Compaq Presario F700 */ + CXT5051_TOSHIBA, /* Toshiba M300 & co */ CXT5051_MODELS }; @@ -1902,12 +1923,14 @@ static const char *cxt5051_models[CXT5051_MODELS] = { [CXT5051_HP_DV6736] = "hp-dv6736", [CXT5051_LENOVO_X200] = "lenovo-x200", [CXT5051_F700] = "hp-700", + [CXT5051_TOSHIBA] = "toshiba", }; static struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736), SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP), SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700), + SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), @@ -1950,6 +1973,7 @@ static int patch_cxt5051(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, cxt5051_models, cxt5051_cfg_tbl); + spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC; switch (board_config) { case CXT5051_HP: spec->mixers[0] = cxt5051_hp_mixers; @@ -1957,7 +1981,7 @@ static int patch_cxt5051(struct hda_codec *codec) case CXT5051_HP_DV6736: spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs; spec->mixers[0] = cxt5051_hp_dv6736_mixers; - spec->no_auto_mic = 1; + spec->auto_mic = 0; break; case CXT5051_LENOVO_X200: spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; @@ -1965,7 +1989,11 @@ static int patch_cxt5051(struct hda_codec *codec) case CXT5051_F700: spec->init_verbs[0] = cxt5051_f700_init_verbs; spec->mixers[0] = cxt5051_f700_mixers; - spec->no_auto_mic = 1; + spec->auto_mic = 0; + break; + case CXT5051_TOSHIBA: + spec->mixers[0] = cxt5051_toshiba_mixers; + spec->auto_mic = AUTO_MIC_PORTB; break; } -- cgit v1.2.2 From 2c7a3fb3f81df7318c70d2b8ecbd87f008e28d52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Jan 2010 10:47:02 +0100 Subject: ALSA: hda - Merge playback controls for Cx5051 codec models All cx5051 codec models have the same Master playback mixer definitions. Merge them together. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 62 +++++++++--------------------------------- 1 file changed, 13 insertions(+), 49 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 56dda9c7f899..e24bec6ca23a 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1689,13 +1689,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, conexant_report_jack(codec, nid); } -static struct snd_kcontrol_new cxt5051_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), +static struct snd_kcontrol_new cxt5051_playback_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1705,7 +1699,16 @@ static struct snd_kcontrol_new cxt5051_mixers[] = { .put = cxt5051_hp_master_sw_put, .private_value = 0x1a, }, + {} +}; +static struct snd_kcontrol_new cxt5051_capture_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), {} }; @@ -1714,48 +1717,18 @@ static struct snd_kcontrol_new cxt5051_hp_mixers[] = { HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT), HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} }; static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = { HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} }; static struct snd_kcontrol_new cxt5051_f700_mixers[] = { HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} }; @@ -1764,16 +1737,6 @@ static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = { HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = cxt_eapd_info, - .get = cxt_eapd_get, - .put = cxt5051_hp_master_sw_put, - .private_value = 0x1a, - }, - {} }; @@ -1958,8 +1921,9 @@ static int patch_cxt5051(struct hda_codec *codec) spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; spec->num_adc_nids = 1; /* not 2; via auto-mic switch */ spec->adc_nids = cxt5051_adc_nids; - spec->num_mixers = 1; - spec->mixers[0] = cxt5051_mixers; + spec->num_mixers = 2; + spec->mixers[0] = cxt5051_capture_mixers; + spec->mixers[1] = cxt5051_playback_mixers; spec->num_init_verbs = 1; spec->init_verbs[0] = cxt5051_init_verbs; spec->spdif_route = 0; -- cgit v1.2.2 From 6953e5524a2ee0dcf57a83d8a6728d1262c54c37 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Jan 2010 11:00:27 +0100 Subject: ALSA: hda - initialize mic port on cxt5051 codec dynamically Initialize the mic ports B & C on Conexant 5051 codec dynamically according to the mic jack detection, instead of static init arrays. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e24bec6ca23a..4fbb398ccd67 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1765,8 +1765,6 @@ static struct hda_verb cxt5051_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, { } /* end */ }; @@ -1792,7 +1790,6 @@ static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, { } /* end */ }; @@ -1824,8 +1821,6 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, - {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, { } /* end */ }; @@ -1852,15 +1847,34 @@ static struct hda_verb cxt5051_f700_init_verbs[] = { /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, { } /* end */ }; +static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, + unsigned int event) +{ + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | event); +#ifdef CONFIG_SND_HDA_INPUT_JACK + conexant_add_jack(codec, nid, SND_JACK_MICROPHONE); + conexant_report_jack(codec, nid); +#endif +} + /* initialize jack-sensing, too */ static int cxt5051_init(struct hda_codec *codec) { + struct conexant_spec *spec = codec->spec; + conexant_init(codec); conexant_init_jacks(codec); + + if (spec->auto_mic & AUTO_MIC_PORTB) + cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT); + if (spec->auto_mic & AUTO_MIC_PORTC) + cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT); + if (codec->patch_ops.unsol_event) { cxt5051_hp_automute(codec); cxt5051_portb_automic(codec); -- cgit v1.2.2 From ecda0cff9df77d3f7d388bd4966e61f1947d2c95 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Jan 2010 11:14:36 +0100 Subject: ALSA: hda - Fix SPDIF output widget for Cxt5051 codec Fixed the wrongly set up for SPDIF output on Conexant 5051 codec. It must point to the audio out widget instead of a pin. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4fbb398ccd67..250b74f8136e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -42,7 +42,7 @@ /* Conexant 5051 specific */ -#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_SPDIF_OUT 0x12 #define CXT5051_PORTB_EVENT 0x38 #define CXT5051_PORTC_EVENT 0x39 -- cgit v1.2.2 From 23d2df5b0db67fa90d3caf4b2d2f21ca33ec9c11 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Jan 2010 11:19:27 +0100 Subject: ALSA: hda - Change headphone pin control with master volume on cx5051 The HP pin (0x16) control has to be changed dynamically depending on the master volume switch as well as the speaker pin (0x1a). Otherwise the headphone still sounds with master off. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 250b74f8136e..9077e4174ee6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1605,6 +1605,11 @@ static void cxt5051_update_speaker(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; unsigned int pinctl; + /* headphone pin */ + pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); + /* speaker pin */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); -- cgit v1.2.2 From 973b8cb0ead3e0b1dd3ee7b2df52e4dff1ffc707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wojni=C5=82owicz?= Date: Sun, 24 Jan 2010 14:12:37 +0100 Subject: ALSA: hda - add possibility to choose speakers configuration for 4930g MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now one can choose speaker configuration in e.g. PulseAudio mixer Signed-off-by: Łukasz Wojniłowicz Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d00e6d1da085..da34095c707f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9478,6 +9478,7 @@ static struct alc_config_preset alc882_presets[] = { .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), .channel_mode = alc883_3ST_6ch_modes, .need_dac_fix = 1, + .const_channel_count = 6, .num_mux_defs = ARRAY_SIZE(alc888_2_capture_sources), .input_mux = alc888_2_capture_sources, -- cgit v1.2.2 From 95f475f7a2e5d60fe9eeb7a2700753036a6ee6a0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 Jan 2010 15:41:11 +0100 Subject: ALSA: hda - Remove coef output in Realtek proc files The output of COEF index/value in the proc file for Realtek codecs is rather useless since the value varies together with the index. Let's get rid of it again. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c53faa959390..a3d223894642 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -841,27 +841,6 @@ static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) spec->init_verbs[spec->num_init_verbs++] = verb; } -#ifdef CONFIG_PROC_FS -/* - * hook for proc - */ -static void print_realtek_coef(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int coeff; - - if (nid != 0x20) - return; - coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0); - snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); - coeff = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_COEF_INDEX, 0); - snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); -} -#else -#define print_realtek_coef NULL -#endif - /* * set up from the preset table */ @@ -5078,7 +5057,6 @@ static int patch_alc880(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -6688,7 +6666,6 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc260_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -10306,7 +10283,6 @@ static int patch_alc882(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc882_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -12170,7 +12146,6 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc262_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -13237,8 +13212,6 @@ static int patch_alc268(struct hda_codec *codec) if (board_config == ALC268_AUTO) spec->init_hook = alc268_auto_init; - codec->proc_widget_hook = print_realtek_coef; - return 0; } @@ -13955,7 +13928,6 @@ static int patch_alc269(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc269_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -15083,7 +15055,6 @@ static int patch_alc861(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc861_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -16063,7 +16034,6 @@ static int patch_alc861vd(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc861vd_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -18198,7 +18168,6 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc662_loopbacks; #endif - codec->proc_widget_hook = print_realtek_coef; return 0; } -- cgit v1.2.2 From 0aea778efa0d632b62eb35122cbb3b9fae548c61 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 Jan 2010 15:44:11 +0100 Subject: ALSA: hda - Remove the COEF setup for ALC267/ALC268 The COEF setup for model=auto seems problematic on some laptops, resulting in the silent speaker output. Better to disable it for now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a3d223894642..b2f543d3b833 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1145,6 +1145,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0888: alc888_coef_init(codec); break; +#if 0 /* XXX: This may cause the silent output on speaker on some machines */ case 0x10ec0267: case 0x10ec0268: snd_hda_codec_write(codec, 0x20, 0, @@ -1157,6 +1158,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) AC_VERB_SET_PROC_COEF, tmp | 0x3000); break; +#endif /* XXX */ } break; } -- cgit v1.2.2 From cf944ee55cc318bdb1d4b2f3f5cce3257f7c07b3 Mon Sep 17 00:00:00 2001 From: Florian Zumbiehl Date: Tue, 26 Jan 2010 09:06:14 +0100 Subject: ALSA: cs46xx: Fix cpu idling with resume Make sure that capture DMA doesn't stay enabled after system resume as that potentially prevents the processor from entering deep sleep states. Signed-off-by: Florian Zumbiehl Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index e6b4a879ae2e..56fcf00c0e27 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3644,6 +3644,7 @@ int snd_cs46xx_resume(struct pci_dev *pci) #ifdef CONFIG_SND_CS46XX_NEW_DSP int i; #endif + unsigned int tmp; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); @@ -3685,6 +3686,15 @@ int snd_cs46xx_resume(struct pci_dev *pci) snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); + /* + * Stop capture DMA. + */ + tmp = snd_cs46xx_peek(chip, BA1_CCTL); + chip->capt.ctl = tmp & 0x0000ffff; + snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000); + + mdelay(5); + /* reset playback/capture */ snd_cs46xx_set_play_sample_rate(chip, 8000); snd_cs46xx_set_capture_sample_rate(chip, 8000); -- cgit v1.2.2 From ccc5df058da70d1c26c72cd1c24072a89998d735 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Tue, 26 Jan 2010 15:59:33 +0800 Subject: ALSA: hda - Add support for more the 8 streams In azx_stream_start() and azx_stream_stop(), it use azx_readb/azx_writeb to read/write SIE, it just enable/disable 8 streams. But according to the HDA spec, it support 30 streams, and the new HDA controller will support more then 8 streams. So we should use azx_readl/azx_writel to read/write SIE. Signed-off-by: Wei Ni Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6d331c4cf185..6eeefda63838 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -954,8 +954,8 @@ static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) azx_dev->insufficient = 1; /* enable SIE */ - azx_writeb(chip, INTCTL, - azx_readb(chip, INTCTL) | (1 << azx_dev->index)); + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) | (1 << azx_dev->index)); /* set DMA start and interrupt mask */ azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_DMA_START | SD_INT_MASK); @@ -974,8 +974,8 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) { azx_stream_clear(chip, azx_dev); /* disable SIE */ - azx_writeb(chip, INTCTL, - azx_readb(chip, INTCTL) & ~(1 << azx_dev->index)); + azx_writel(chip, INTCTL, + azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); } -- cgit v1.2.2 From 8ce28d6abff34886d3797b25324c940471b99164 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 27 Jan 2010 20:26:08 +0100 Subject: ALSA: hda - Add an ASUS mobo to MSI blacklist Sid Boyce reported that his machine locks up without enable_msi=0 option. This looks like another ASUS mobo with Nvidia combo. Reported-by: Sid Boyce Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index ec9c348336cc..565de38a3fc7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2332,6 +2332,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) */ static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ + SND_PCI_QUIRK(0x1043, 0x829c, "ASUS", 0), /* nvidia */ {} }; -- cgit v1.2.2 From b09f3e78ee7bb69171411b75bd9e771fc7f24749 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 Jan 2010 00:01:53 +0100 Subject: ALSA: hda - Allow override more fields via patch loader Allow the override of vendor-id, subsystem-id, revision-id and chip name via patch loading. Updated the document, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_hwdep.c | 53 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index b36919c0d363..a1fc83753cc6 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -625,6 +625,10 @@ enum { LINE_MODE_PINCFG, LINE_MODE_VERB, LINE_MODE_HINT, + LINE_MODE_VENDOR_ID, + LINE_MODE_SUBSYSTEM_ID, + LINE_MODE_REVISION_ID, + LINE_MODE_CHIP_NAME, NUM_LINE_MODES, }; @@ -654,53 +658,71 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, } /* parse the contents after the other command tags, [pincfg], [verb], - * [hint] and [model] + * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model] * just pass to the sysfs helper (only when any codec was specified) */ static void parse_pincfg_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_user_pin_configs(*codecp, buf); } static void parse_verb_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_init_verbs(*codecp, buf); } static void parse_hint_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; parse_hints(*codecp, buf); } static void parse_model_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - if (!*codecp) - return; kfree((*codecp)->modelname); (*codecp)->modelname = kstrdup(buf, GFP_KERNEL); } +static void parse_chip_name_mode(char *buf, struct hda_bus *bus, + struct hda_codec **codecp) +{ + kfree((*codecp)->chip_name); + (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); +} + +#define DEFINE_PARSE_ID_MODE(name) \ +static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ + struct hda_codec **codecp) \ +{ \ + unsigned long val; \ + if (!strict_strtoul(buf, 0, &val)) \ + (*codecp)->name = val; \ +} + +DEFINE_PARSE_ID_MODE(vendor_id); +DEFINE_PARSE_ID_MODE(subsystem_id); +DEFINE_PARSE_ID_MODE(revision_id); + + struct hda_patch_item { const char *tag; void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc); + int need_codec; }; static struct hda_patch_item patch_items[NUM_LINE_MODES] = { - [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode }, - [LINE_MODE_MODEL] = { "[model]", parse_model_mode }, - [LINE_MODE_VERB] = { "[verb]", parse_verb_mode }, - [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode }, - [LINE_MODE_HINT] = { "[hint]", parse_hint_mode }, + [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 }, + [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 }, + [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 }, + [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 }, + [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 }, + [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 }, + [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 }, + [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 }, + [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 }, }; /* check the line starting with '[' -- change the parser mode accodingly */ @@ -783,7 +805,8 @@ int snd_hda_load_patch(struct hda_bus *bus, const char *patch) continue; if (*buf == '[') line_mode = parse_line_mode(buf, bus); - else if (patch_items[line_mode].parser) + else if (patch_items[line_mode].parser && + (codec || !patch_items[line_mode].need_codec)) patch_items[line_mode].parser(buf, bus, &codec); } release_firmware(fw); -- cgit v1.2.2 From 7b36ea967cc5b5088a57fe225f1f72a3c160058b Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Thu, 28 Jan 2010 16:13:07 +0800 Subject: ALSA: hda - Change the AZX_MAX_PCMS to 10 In hda_codec.c, it has define "[HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },", it support up to device 9 for HDMI. But in hda_intel.c, it only define AZX_MAX_PCMS as 8. So if it have 4 hdmi codecs, when run azx_attach_pcm_stream(), it will show error "Invalid PCM device number 8", and "... number 9", and return "-EINVAL". We should change the AZX_MAX_PCMS to 10. Signed-off-by: Wei Ni Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6eeefda63838..170126c28abd 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -261,7 +261,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* 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_PCMS 8 +#define AZX_MAX_PCMS 10 /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 -- cgit v1.2.2 From c89362225152fc6f2247f65371bfe3ccced3203b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 Jan 2010 17:08:53 +0100 Subject: ALSA: hda - Define max number of PCM devices in hda_codec.h Define the constant rather in the common header file. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_intel.c | 10 ++++------ 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 26ceace88c96..98767df4f03a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3275,6 +3275,8 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { /* * get the empty PCM device number to assign + * + * note the max device number is limited by HDA_MAX_PCMS, currently 10 */ static int get_empty_pcm_device(struct hda_bus *bus, int type) { diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0c8f05cc56be..b75da47571e6 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -527,6 +527,9 @@ enum { /* max. codec address */ #define HDA_MAX_CODEC_ADDRESS 0x0f +/* max number of PCM devics per card */ +#define HDA_MAX_PCMS 10 + /* * generic arrays */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 170126c28abd..12230a2ed4f1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -260,8 +260,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #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_PCMS 10 /* RIRB int mask: overrun[2], response[0] */ #define RIRB_INT_RESPONSE 0x01 @@ -409,7 +407,7 @@ struct azx { struct azx_dev *azx_dev; /* PCM */ - struct snd_pcm *pcm[AZX_MAX_PCMS]; + struct snd_pcm *pcm[HDA_MAX_PCMS]; /* HD codec */ unsigned short codec_mask; @@ -1336,7 +1334,7 @@ static void azx_bus_reset(struct hda_bus *bus) if (chip->initialized) { int i; - for (i = 0; i < AZX_MAX_PCMS; i++) + for (i = 0; i < HDA_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); snd_hda_suspend(chip->bus); snd_hda_resume(chip->bus); @@ -1966,7 +1964,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, int pcm_dev = cpcm->device; int s, err; - if (pcm_dev >= AZX_MAX_PCMS) { + if (pcm_dev >= HDA_MAX_PCMS) { snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", pcm_dev); return -EINVAL; @@ -2122,7 +2120,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); - for (i = 0; i < AZX_MAX_PCMS; i++) + for (i = 0; i < HDA_MAX_PCMS; i++) snd_pcm_suspend_all(chip->pcm[i]); if (chip->initialized) snd_hda_suspend(chip->bus); -- cgit v1.2.2 From 30ed7ed11cb88fd56d821a67b9aab1e0d50fb626 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 28 Jan 2010 17:11:45 +0100 Subject: ALSA: hda - Fix index of HP Compaq F700 mic amp The amp used for the mic input on HP Compaq F700 with Cxt5051 codec has no multiple inputs, thus its index should be 0 instead of 1. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 9077e4174ee6..745e35992144 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1832,7 +1832,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { static struct hda_verb cxt5051_f700_init_verbs[] = { /* Line in, Mic */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0}, -- cgit v1.2.2 From e108c7b79e91b45a3f04762c44fd404a5d9be069 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Thu, 28 Jan 2010 19:21:07 +0100 Subject: ALSA: hda - Add mute LED check for HP laptops with IDT 92HD83xxx codec This patch adds HP mute LED support for IDT 92HD81/3 family of the codecs. Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index dbffb5b5c69d..cb9802f4b063 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5332,6 +5332,11 @@ again: if (spec->board_config == STAC_92HD83XXX_HP) spec->gpio_led = 0x01; + if (find_mute_led_gpio(codec)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { spec->gpio_mask |= spec->gpio_led; -- cgit v1.2.2 From 36706005d90642bccabfaacbb24d135155e984a8 Mon Sep 17 00:00:00 2001 From: Charles Chin Date: Fri, 29 Jan 2010 12:05:51 +0100 Subject: ALSA: hda - Add support for IDT 92HD88 family codecs Signed-off-by: Charles Chin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index cb9802f4b063..9694675f0b9e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -568,6 +568,11 @@ static hda_nid_t stac92hd83xxx_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x1f, 0x20, }; +static hda_nid_t stac92hd88xxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0f, 0x11, 0x1f, 0x20, +}; + #define STAC92HD71BXX_NUM_PINS 13 static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, @@ -2873,6 +2878,13 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); + /* 92HD88: trace back up the link of nids to find the DAC */ + while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0])) + != AC_WID_AUD_OUT)) { + nid = conn[0]; + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + } for (j = 0; j < conn_len; j++) { wcaps = get_wcaps(codec, conn[j]); wtype = get_wcaps_type(wcaps); @@ -5318,6 +5330,16 @@ again: stac92hd83xxx_brd_tbl[spec->board_config]); switch (codec->vendor_id) { + case 0x111d7666: + case 0x111d7667: + case 0x111d7668: + case 0x111d7669: + spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids); + spec->pin_nids = stac92hd88xxx_pin_nids; + spec->mono_nid = 0; + spec->digbeep_nid = 0; + spec->num_pwrs = 0; + break; case 0x111d7604: case 0x111d7605: case 0x111d76d5: @@ -6243,6 +6265,10 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx}, + { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx}, { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx}, { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, -- cgit v1.2.2 From a9694faa287888b4fb10849649b6c94d0a1c9940 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Thu, 4 Feb 2010 08:58:23 +0100 Subject: ALSA: hda - Adding support for another IDT 92HD83XXX codec Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9694675f0b9e..693dd14d9ec1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5341,6 +5341,7 @@ again: spec->num_pwrs = 0; break; case 0x111d7604: + case 0x111d76d4: case 0x111d7605: case 0x111d76d5: if (spec->board_config == STAC_92HD83XXX_PWR_REF) @@ -6263,6 +6264,7 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, + { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx}, { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx}, { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx}, { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx}, -- cgit v1.2.2 From 04b5efe5fa7f71c37b938053666fac317b67c636 Mon Sep 17 00:00:00 2001 From: Charles Chin Date: Thu, 4 Feb 2010 10:28:02 +0100 Subject: ALSA: hda - Fix docking output for IDT 92HD8xx codecs This patch fixes docking output support for IDT 92HD81/83/88 family codecs. Typically one of ports 0xE or 0xF is used for docking output, while only port 0xF is common on all the three codec families. We don't want the pin to select the analog mixer here. Signed-off-by: Charles Chin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 693dd14d9ec1..834c5980fe5d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5291,7 +5291,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) hda_nid_t conn[STAC92HD83_DAC_COUNT + 1]; int err; int num_dacs; - hda_nid_t nid; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -5387,24 +5386,21 @@ again: return err; } - switch (spec->board_config) { - case STAC_DELL_S14: - nid = 0xf; - break; - default: - nid = 0xe; - break; - } - - num_dacs = snd_hda_get_connections(codec, nid, + /* docking output support */ + num_dacs = snd_hda_get_connections(codec, 0xF, conn, STAC92HD83_DAC_COUNT + 1) - 1; - if (num_dacs < 0) - num_dacs = STAC92HD83_DAC_COUNT; - - /* set port X to select the last DAC - */ - snd_hda_codec_write_cache(codec, nid, 0, + /* skip non-DAC connections */ + while (num_dacs >= 0 && + (get_wcaps_type(get_wcaps(codec, conn[num_dacs])) + != AC_WID_AUD_OUT)) + num_dacs--; + /* set port E and F to select the last DAC */ + if (num_dacs >= 0) { + snd_hda_codec_write_cache(codec, 0xE, 0, + AC_VERB_SET_CONNECT_SEL, num_dacs); + snd_hda_codec_write_cache(codec, 0xF, 0, AC_VERB_SET_CONNECT_SEL, num_dacs); + } codec->proc_widget_hook = stac92hd_proc_hook; -- cgit v1.2.2 From 88102f3f841b680412714d0b0b7da33c2a00c1f9 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 4 Feb 2010 14:12:58 +0100 Subject: ALSA: hda - Remove superfluous init verb entries for ALC88[235] The default values are no need to be set in init_verbs. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 75 +++++++------------------------------------ 1 file changed, 12 insertions(+), 63 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b2f543d3b833..40ebf2746bb1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7332,29 +7332,18 @@ static struct snd_kcontrol_new alc882_chmode_mixer[] = { static struct hda_verb alc882_base_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* mute analog input loopbacks */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Front Pin: output 0 (0x0c) */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -7391,14 +7380,8 @@ static struct hda_verb alc882_base_init_verbs[] = { /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Input mixer3 */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* ADC2: mute amp left and right */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -7442,26 +7425,17 @@ static struct hda_verb alc_hp15_unsol_verbs[] = { static struct hda_verb alc885_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {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_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* Rear mixer */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* CLFE mixer */ - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* Side mixer */ - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - - /* mute analog input loopbacks */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* Front HP Pin: output 0 (0x0c) */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, @@ -7495,17 +7469,11 @@ static struct hda_verb alc885_init_verbs[] = { /* Mixer elements: 0x18, , 0x1a, 0x1b */ /* Input mixer1 */ - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* Input mixer2 */ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* ADC2: mute amp left and right */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* ADC3: mute amp left and right */ @@ -7991,18 +7959,6 @@ static struct hda_verb alc883_auto_init_verbs[] = { {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for - * front panel mic (mic 2) - */ - /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* * Set up output mixers (0x0c - 0x0f) */ @@ -8027,16 +7983,9 @@ static struct hda_verb alc883_auto_init_verbs[] = { /* FIXME: use matrix-type input source selection */ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* Input mixer3 */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))}, - + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { } }; -- cgit v1.2.2 From 84898e87cc0fff976202d5b91656f2db949fc2dd Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 4 Feb 2010 14:16:14 +0100 Subject: ALSA: hda - Add ALC269VB support - Add new models ALC269VB_AMIC ALC269VB_DMIC - Add alc269vb_laptop_dmic_setup The record source index Dmic is 0x6 for ALC269VB. - Change eeepc words for ALC269 - Modify init_verb tables of patch_alc269 patch_alc662 patch_alc882 - Modify common patch for ALC270 ALC269VB ALC275 Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 346 ++++++++++++++++++++++++++++++------------ 1 file changed, 246 insertions(+), 100 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 40ebf2746bb1..826ecdbdd2bb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -131,8 +131,10 @@ enum { enum { ALC269_BASIC, ALC269_QUANTA_FL1, - ALC269_ASUS_AMIC, - ALC269_ASUS_DMIC, + ALC269_AMIC, + ALC269_DMIC, + ALC269VB_AMIC, + ALC269VB_DMIC, ALC269_FUJITSU, ALC269_LIFEBOOK, ALC269_AUTO, @@ -13182,6 +13184,15 @@ static hda_nid_t alc269_capsrc_nids[1] = { 0x23, }; +static hda_nid_t alc269vb_adc_nids[1] = { + /* ADC1 */ + 0x09, +}; + +static hda_nid_t alc269vb_capsrc_nids[1] = { + 0x22, +}; + /* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24), * not a mux! */ @@ -13250,7 +13261,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = { { } }; -static struct snd_kcontrol_new alc269_eeepc_mixer[] = { +static struct snd_kcontrol_new alc269_laptop_mixer[] = { HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), @@ -13258,16 +13269,47 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc269vb_laptop_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + { } /* end */ +}; + /* capture mixer elements */ -static struct snd_kcontrol_new alc269_epc_capture_mixer[] = { +static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), { } /* end */ }; /* FSC amilo */ -#define alc269_fujitsu_mixer alc269_eeepc_mixer +#define alc269_fujitsu_mixer alc269_laptop_mixer static struct hda_verb alc269_quanta_fl1_verbs[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -13410,7 +13452,7 @@ static void alc269_lifebook_init_hook(struct hda_codec *codec) alc269_lifebook_mic_autoswitch(codec); } -static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { +static struct hda_verb alc269_laptop_dmic_init_verbs[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, @@ -13421,7 +13463,7 @@ static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { {} }; -static struct hda_verb alc269_eeepc_amic_init_verbs[] = { +static struct hda_verb alc269_laptop_amic_init_verbs[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, @@ -13431,6 +13473,28 @@ static struct hda_verb alc269_eeepc_amic_init_verbs[] = { {} }; +static struct hda_verb alc269vb_laptop_dmic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x06}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + +static struct hda_verb alc269vb_laptop_amic_init_verbs[] = { + {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 }, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + /* toggle speaker-output according to the hp-jack state */ static void alc269_speaker_automute(struct hda_codec *codec) { @@ -13448,7 +13512,7 @@ static void alc269_speaker_automute(struct hda_codec *codec) } /* unsolicited event for HP jack sensing */ -static void alc269_eeepc_unsol_event(struct hda_codec *codec, +static void alc269_laptop_unsol_event(struct hda_codec *codec, unsigned int res) { switch (res >> 26) { @@ -13461,7 +13525,7 @@ static void alc269_eeepc_unsol_event(struct hda_codec *codec, } } -static void alc269_eeepc_dmic_setup(struct hda_codec *codec) +static void alc269_laptop_dmic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->ext_mic.pin = 0x18; @@ -13471,7 +13535,17 @@ static void alc269_eeepc_dmic_setup(struct hda_codec *codec) spec->auto_mic = 1; } -static void alc269_eeepc_amic_setup(struct hda_codec *codec) +static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 6; + spec->auto_mic = 1; +} + +static void alc269_laptop_amic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->ext_mic.pin = 0x18; @@ -13481,7 +13555,7 @@ static void alc269_eeepc_amic_setup(struct hda_codec *codec) spec->auto_mic = 1; } -static void alc269_eeepc_inithook(struct hda_codec *codec) +static void alc269_laptop_inithook(struct hda_codec *codec) { alc269_speaker_automute(codec); alc_mic_automute(codec); @@ -13494,22 +13568,10 @@ static struct hda_verb alc269_init_verbs[] = { /* * Unmute ADC0 and set the default input to mic-in */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the - * analog-loopback mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for - * front panel mic (mic 2) - */ - /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* - * Set up output mixers (0x0c - 0x0e) + * Set up output mixers (0x02 - 0x03) */ /* set vol=0 to output mixers */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, @@ -13534,26 +13596,57 @@ static struct hda_verb alc269_init_verbs[] = { {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* FIXME: use Mux-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* FIXME: use matrix-type input source selection */ + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +static struct hda_verb alc269vb_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* + * Set up output mixers (0x02 - 0x03) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* FIXME: use Mux-type input source selection */ /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - {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)}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set EAPD */ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, - {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, { } }; @@ -13601,6 +13694,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err; static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + hda_nid_t real_capsrc_nids; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, alc269_ignore); @@ -13622,11 +13716,20 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->kctls.list) add_mixer(spec, spec->kctls.list); - add_verb(spec, alc269_init_verbs); + if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { + add_verb(spec, alc269vb_init_verbs); + real_capsrc_nids = alc269vb_capsrc_nids[0]; + alc_ssid_check(codec, 0x21, 0x1b, 0x14); + } else { + add_verb(spec, alc269_init_verbs); + real_capsrc_nids = alc269_capsrc_nids[0]; + alc_ssid_check(codec, 0x15, 0x1b, 0x14); + } + spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; /* set default input source */ - snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0], + snd_hda_codec_write_cache(codec, real_capsrc_nids, 0, AC_VERB_SET_CONNECT_SEL, spec->input_mux->items[0].index); @@ -13637,8 +13740,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (!spec->cap_mixer && !spec->no_analog) set_capture_mixer(codec); - alc_ssid_check(codec, 0x15, 0x1b, 0x14); - return 1; } @@ -13664,8 +13765,8 @@ static void alc269_auto_init(struct hda_codec *codec) static const char *alc269_models[ALC269_MODEL_LAST] = { [ALC269_BASIC] = "basic", [ALC269_QUANTA_FL1] = "quanta", - [ALC269_ASUS_AMIC] = "asus-amic", - [ALC269_ASUS_DMIC] = "asus-dmic", + [ALC269_AMIC] = "laptop-amic", + [ALC269_DMIC] = "laptop-dmic", [ALC269_FUJITSU] = "fujitsu", [ALC269_LIFEBOOK] = "lifebook", [ALC269_AUTO] = "auto", @@ -13674,41 +13775,49 @@ static const char *alc269_models[ALC269_MODEL_LAST] = { static struct snd_pci_quirk alc269_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1), SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A", - ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80JT", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_AMIC), - SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_AMIC), + ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC), + SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), + SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", - ALC269_ASUS_DMIC), + ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_ASUS_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_ASUS_DMIC), + ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), {} @@ -13738,47 +13847,75 @@ static struct alc_config_preset alc269_presets[] = { .setup = alc269_quanta_fl1_setup, .init_hook = alc269_quanta_fl1_init_hook, }, - [ALC269_ASUS_AMIC] = { - .mixers = { alc269_eeepc_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + [ALC269_AMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_analog_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_amic_init_verbs }, + alc269_laptop_amic_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), .dac_nids = alc269_dac_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .unsol_event = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_amic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc269_laptop_inithook, }, - [ALC269_ASUS_DMIC] = { - .mixers = { alc269_eeepc_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + [ALC269_DMIC] = { + .mixers = { alc269_laptop_mixer }, + .cap_mixer = alc269_laptop_digital_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_dmic_init_verbs }, + alc269_laptop_dmic_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), .dac_nids = alc269_dac_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .unsol_event = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_dmic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc269_laptop_inithook, + }, + [ALC269VB_AMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_analog_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_amic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_amic_setup, + .init_hook = alc269_laptop_inithook, + }, + [ALC269VB_DMIC] = { + .mixers = { alc269vb_laptop_mixer }, + .cap_mixer = alc269vb_laptop_digital_capture_mixer, + .init_verbs = { alc269vb_init_verbs, + alc269vb_laptop_dmic_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269vb_laptop_dmic_setup, + .init_hook = alc269_laptop_inithook, }, [ALC269_FUJITSU] = { .mixers = { alc269_fujitsu_mixer }, - .cap_mixer = alc269_epc_capture_mixer, + .cap_mixer = alc269_laptop_digital_capture_mixer, .init_verbs = { alc269_init_verbs, - alc269_eeepc_dmic_init_verbs }, + alc269_laptop_dmic_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), .dac_nids = alc269_dac_nids, .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, - .unsol_event = alc269_eeepc_unsol_event, - .setup = alc269_eeepc_dmic_setup, - .init_hook = alc269_eeepc_inithook, + .unsol_event = alc269_laptop_unsol_event, + .setup = alc269_laptop_dmic_setup, + .init_hook = alc269_laptop_inithook, }, [ALC269_LIFEBOOK] = { .mixers = { alc269_lifebook_mixer }, @@ -13799,6 +13936,7 @@ static int patch_alc269(struct hda_codec *codec) struct alc_spec *spec; int board_config; int err; + int is_alc269vb = 0; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -13815,6 +13953,7 @@ static int patch_alc269(struct hda_codec *codec) alc_free(codec); return -ENOMEM; } + is_alc269vb = 1; } board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, @@ -13850,7 +13989,7 @@ static int patch_alc269(struct hda_codec *codec) if (board_config != ALC269_AUTO) setup_preset(codec, &alc269_presets[board_config]); - if (codec->subsystem_id == 0x17aa3bf8) { + if (board_config == ALC269_QUANTA_FL1) { /* Due to a hardware problem on Lenovo Ideadpad, we need to * fix the sample rate of analog I/O to 44.1kHz */ @@ -13863,9 +14002,16 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_playback = &alc269_pcm_digital_playback; spec->stream_digital_capture = &alc269_pcm_digital_capture; - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; + if (!is_alc269vb) { + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->capsrc_nids = alc269_capsrc_nids; + } else { + spec->adc_nids = alc269vb_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); + spec->capsrc_nids = alc269vb_capsrc_nids; + } + if (!spec->cap_mixer) set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); -- cgit v1.2.2 From cec27c891b805b2ab2302f9fcbdacb6f179ac0d4 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 4 Feb 2010 14:18:18 +0100 Subject: ALSA: hda - Add support of ALC665 - Add support for ALC665 - Add more ASUS model - Modify common patch for ALC272 ALC273 ALC661 ALC662 ALC663 ALC665 Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 112 +++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 68 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 826ecdbdd2bb..82772f0ab3e7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16597,13 +16597,6 @@ static struct hda_verb alc662_init_verbs[] = { /* ADC: mute amp left and right */ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Front mixer: unmute input/output amp left and right (volume = 0) */ - - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -16653,6 +16646,28 @@ static struct hda_verb alc662_init_verbs[] = { { } }; +static struct hda_verb alc663_init_verbs[] = { + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } +}; + +static struct hda_verb alc272_init_verbs[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } +}; + static struct hda_verb alc662_sue_init_verbs[] = { {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, @@ -16672,61 +16687,6 @@ static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { {} }; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb alc662_auto_init_verbs[] = { - /* - * Unmute ADC and set the default input to mic-in - */ - {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback - * mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for front - * panel mic (mic 2) - */ - /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - - /* - * Set up output mixers (0x0c - 0x0f) - */ - /* set vol=0 to output mixers */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - - /* set up input amps for analog loopback */ - /* Amp Indices: DAC = 0, mixer = 1 */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - - - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ - /* Input mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -/* additional verbs for ALC663 */ -static struct hda_verb alc663_auto_init_verbs[] = { - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } -}; - static struct hda_verb alc663_m51va_init_verbs[] = { {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, @@ -17477,6 +17437,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1), SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), + SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), @@ -17512,6 +17473,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), @@ -18157,9 +18119,13 @@ static int alc662_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - add_verb(spec, alc662_auto_init_verbs); - if (codec->vendor_id == 0x10ec0663) - add_verb(spec, alc663_auto_init_verbs); + add_verb(spec, alc662_init_verbs); + if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || + codec->vendor_id == 0x10ec0665) + add_verb(spec, alc663_init_verbs); + + if (codec->vendor_id == 0x10ec0272) + add_verb(spec, alc272_init_verbs); err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -18251,11 +18217,20 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->cap_mixer) set_capture_mixer(codec); - if (codec->vendor_id == 0x10ec0662) + + switch (codec->vendor_id) { + case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - else + break; + case 0x10ec0272: + case 0x10ec0663: + case 0x10ec0665: set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - + break; + case 0x10ec0273: + set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + break; + } spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; @@ -18305,6 +18280,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", .patch = patch_alc662 }, { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, + { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, -- cgit v1.2.2 From 21956b61f594f7924d98240da74bc81c28601fa9 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 2 Feb 2010 19:58:25 +0100 Subject: ALSA: ctxfi - fix PTP address initialization After hours of debugging, I finally found the reason why some source and runtime combination does not work. The PTP (page table pages) address must be aligned. I am not sure how much, but alignment to PAGE_SIZE is sufficient. Also, use ALSA's page allocation routines to ensure proper virtual -> physical address translation. Cc: Signed-off-by: Jaroslav Kysela --- sound/pci/ctxfi/ctatc.c | 15 ++------------- sound/pci/ctxfi/ctvmem.c | 38 ++++++++++++++++++-------------------- sound/pci/ctxfi/ctvmem.h | 8 +++++--- 3 files changed, 25 insertions(+), 36 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index cb65bd0dd35b..459c1f62783b 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -166,18 +166,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm) static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index) { - struct ct_vm *vm; - void *kvirt_addr; - unsigned long phys_addr; - - vm = atc->vm; - kvirt_addr = vm->get_ptp_virt(vm, index); - if (kvirt_addr == NULL) - phys_addr = (~0UL); - else - phys_addr = virt_to_phys(kvirt_addr); - - return phys_addr; + return atc->vm->get_ptp_phys(atc->vm, index); } static unsigned int convert_format(snd_pcm_format_t snd_format) @@ -1669,7 +1658,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci, } /* Set up device virtual memory management object */ - err = ct_vm_create(&atc->vm); + err = ct_vm_create(&atc->vm, pci); if (err < 0) goto error1; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 6b78752e9503..65da6e466f80 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -138,7 +138,7 @@ ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size) return NULL; } - ptp = vm->ptp[0]; + ptp = (unsigned long *)vm->ptp[0].area; pte_start = (block->addr >> CT_PAGE_SHIFT); pages = block->size >> CT_PAGE_SHIFT; for (i = 0; i < pages; i++) { @@ -158,25 +158,25 @@ static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block) } /* * - * return the host (kmalloced) addr of the @index-th device - * page talbe page on success, or NULL on failure. - * The first returned NULL indicates the termination. + * return the host physical addr of the @index-th device + * page table page on success, or ~0UL on failure. + * The first returned ~0UL indicates the termination. * */ -static void * -ct_get_ptp_virt(struct ct_vm *vm, int index) +static dma_addr_t +ct_get_ptp_phys(struct ct_vm *vm, int index) { - void *addr; + dma_addr_t addr; - addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index]; + addr = (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr; return addr; } -int ct_vm_create(struct ct_vm **rvm) +int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci) { struct ct_vm *vm; struct ct_vm_block *block; - int i; + int i, err = 0; *rvm = NULL; @@ -188,23 +188,21 @@ int ct_vm_create(struct ct_vm **rvm) /* Allocate page table pages */ for (i = 0; i < CT_PTP_NUM; i++) { - vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!vm->ptp[i]) + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(pci), + PAGE_SIZE, &vm->ptp[i]); + if (err < 0) break; } - if (!i) { + if (err < 0) { /* no page table pages are allocated */ - kfree(vm); + ct_vm_destroy(vm); return -ENOMEM; } vm->size = CT_ADDRS_PER_PAGE * i; - /* Initialise remaining ptps */ - for (; i < CT_PTP_NUM; i++) - vm->ptp[i] = NULL; - vm->map = ct_vm_map; vm->unmap = ct_vm_unmap; - vm->get_ptp_virt = ct_get_ptp_virt; + vm->get_ptp_phys = ct_get_ptp_phys; INIT_LIST_HEAD(&vm->unused); INIT_LIST_HEAD(&vm->used); block = kzalloc(sizeof(*block), GFP_KERNEL); @@ -242,7 +240,7 @@ void ct_vm_destroy(struct ct_vm *vm) /* free allocated page table pages */ for (i = 0; i < CT_PTP_NUM; i++) - kfree(vm->ptp[i]); + snd_dma_free_pages(&vm->ptp[i]); vm->size = 0; diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h index 01e4fd0386a3..b23adfca4de6 100644 --- a/sound/pci/ctxfi/ctvmem.h +++ b/sound/pci/ctxfi/ctvmem.h @@ -22,6 +22,8 @@ #include #include +#include +#include /* The chip can handle the page table of 4k pages * (emu20k1 can handle even 8k pages, but we don't use it right now) @@ -41,7 +43,7 @@ struct snd_pcm_substream; /* Virtual memory management object for card device */ struct ct_vm { - void *ptp[CT_PTP_NUM]; /* Device page table pages */ + struct snd_dma_buffer ptp[CT_PTP_NUM]; /* Device page table pages */ unsigned int size; /* Available addr space in bytes */ struct list_head unused; /* List of unused blocks */ struct list_head used; /* List of used blocks */ @@ -52,10 +54,10 @@ struct ct_vm { int size); /* Unmap device logical addr area. */ void (*unmap)(struct ct_vm *, struct ct_vm_block *block); - void *(*get_ptp_virt)(struct ct_vm *vm, int index); + dma_addr_t (*get_ptp_phys)(struct ct_vm *vm, int index); }; -int ct_vm_create(struct ct_vm **rvm); +int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci); void ct_vm_destroy(struct ct_vm *vm); #endif /* CTVMEM_H */ -- cgit v1.2.2 From 350a514787a4516746f738f69bff6aa0d4ac70e9 Mon Sep 17 00:00:00 2001 From: Sebastien Alaiwan Date: Fri, 5 Feb 2010 08:58:20 +0100 Subject: ALSA: ice1712: fix: lock samplerate when samplerate locking is enabled I found that the sampling rate locking setting of the ice1712 sound driver was only half-respected : when the driver was locked to, let's say, 44100Hz, and a usermode app was requesting 48000Hz playback, the request was succesful although the soundcard would continue to run at 44100Hz. Here's a patch that will make those requests to fail. Signed-off-by: Sebastien Alaiwan Signed-off-by: Takashi Iwai --- sound/pci/ice1712/ice1712.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index c7cff6f8168a..fb61943fc4dc 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1180,6 +1180,10 @@ static int snd_ice1712_playback_pro_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (is_pro_rate_locked(ice)) { + runtime->hw.rate_min = PRO_RATE_DEFAULT; + runtime->hw.rate_max = PRO_RATE_DEFAULT; + } if (ice->spdif.ops.open) ice->spdif.ops.open(ice, substream); @@ -1197,6 +1201,11 @@ static int snd_ice1712_capture_pro_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (is_pro_rate_locked(ice)) { + runtime->hw.rate_min = PRO_RATE_DEFAULT; + runtime->hw.rate_max = PRO_RATE_DEFAULT; + } + return 0; } -- cgit v1.2.2 From 1eb6dc7dabcb4aa762d96f4f6978f3ef86321d68 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Thu, 4 Feb 2010 22:21:47 +0200 Subject: ALSA: hda - Delay switching to polling mode if an interrupt was missing My sound codec seems sometimes (very rarely) to omit interrupts (ALC268) However, interrupt mode still works. Thus if we get timeout, poll the codec once. If we get 3 such polls in a row, then switch to polling mode. This patch is maybe an bandaid, but this might be a workaround for hardware bug. Signed-off-by: Maxim Levitsky Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 565de38a3fc7..d853e2c33bb7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -426,6 +426,7 @@ struct azx { /* flags */ int position_fix; + int poll_count; unsigned int running :1; unsigned int initialized :1; unsigned int single_cmd :1; @@ -506,7 +507,7 @@ static char *driver_short_names[] __devinitdata = { #define get_azx_dev(substream) (substream->runtime->private_data) static int azx_acquire_irq(struct azx *chip, int do_disconnect); - +static int azx_send_cmd(struct hda_bus *bus, unsigned int val); /* * Interface for HD codec */ @@ -664,11 +665,12 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, { struct azx *chip = bus->private_data; unsigned long timeout; + int do_poll = 0; again: timeout = jiffies + msecs_to_jiffies(1000); for (;;) { - if (chip->polling_mode) { + if (chip->polling_mode || do_poll) { spin_lock_irq(&chip->reg_lock); azx_update_rirb(chip); spin_unlock_irq(&chip->reg_lock); @@ -676,6 +678,9 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, if (!chip->rirb.cmds[addr]) { smp_rmb(); bus->rirb_error = 0; + + if (!do_poll) + chip->poll_count = 0; return chip->rirb.res[addr]; /* the last value */ } if (time_after(jiffies, timeout)) @@ -688,6 +693,16 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } + if (!chip->polling_mode && chip->poll_count < 2) { + snd_printdd(SFX "azx_get_response timeout, " + "polling the codec once: last cmd=0x%08x\n", + chip->last_cmd[addr]); + do_poll = 1; + chip->poll_count++; + goto again; + } + + if (!chip->polling_mode) { snd_printk(KERN_WARNING SFX "azx_get_response timeout, " "switching to polling mode: last cmd=0x%08x\n", -- cgit v1.2.2 From 9492837a6f54b069e13e40e3c89898bb8837a386 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Thu, 4 Feb 2010 22:26:37 +0200 Subject: ALSA: cosmetic: make hda intel interrupt name consistent with others This renames the interrupt name in /proc/interrupt. HDA Intel -> hda_intel This also eliminates space from the name, probably helping some parsers. Don't think anybody depends on this name in userspace Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d853e2c33bb7..b8faa6dc5abe 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2058,7 +2058,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) { if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, - "HDA Intel", chip)) { + "hda_intel", chip)) { printk(KERN_ERR "hda-intel: unable to grab IRQ %d, " "disabling device\n", chip->pci->irq); if (do_disconnect) -- cgit v1.2.2 From 9d4c7464458770d309169f7a7ce1ea6f8a4a7de5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 5 Feb 2010 10:19:41 +0100 Subject: ALSA: ice1724 - aureon - fix wm8770 volume offset The volume register is from 0..0x7f and 0..0x1a range is mute. Also, fix mute combining in wm_vol_put(). The wrong behaviour was noticed by Peter Christensen. Signed-off-by: Jaroslav Kysela --- sound/pci/ice1712/aureon.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 765d7bd4c3d4..9e66f6d306f8 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -703,11 +703,13 @@ static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned sho { unsigned char nvol; - if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) { nvol = 0; - else + } else { nvol = ((vol % WM_VOL_CNT) * (master % WM_VOL_CNT)) / WM_VOL_MAX; + nvol += 0x1b; + } wm_put(ice, index, nvol); wm_put_nocache(ice, index, 0x180 | nvol); @@ -778,7 +780,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ for (ch = 0; ch < 2; ch++) { unsigned int vol = ucontrol->value.integer.value[ch]; if (vol > WM_VOL_MAX) - continue; + vol = WM_VOL_MAX; vol |= spec->master[ch] & WM_VOL_MUTE; if (vol != spec->master[ch]) { int dac; @@ -834,8 +836,8 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * for (i = 0; i < voices; i++) { unsigned int vol = ucontrol->value.integer.value[i]; if (vol > WM_VOL_MAX) - continue; - vol |= spec->vol[ofs+i]; + vol = WM_VOL_MAX; + vol |= spec->vol[ofs+i] & WM_VOL_MUTE; if (vol != spec->vol[ofs+i]) { spec->vol[ofs+i] = vol; idx = WM_DAC_ATTEN + ofs + i; -- cgit v1.2.2 From 07f804495cb08c8fdf16eee8f7d90edce4a3c9c5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Feb 2010 15:06:13 +0100 Subject: ALSA: hda - Detect HP mute-LED GPIO setup from GPIO counts The GPIO pin number for the mute LED control on HP laptops can be determined more easily by checking the number of available GPIO pins of the codec chip. On a small package with up to 3 GPIOs, GPIO 0 is used while GPIO 3 is used for others. This fixes the missing mute GPIO for some HP laptops with new codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 834c5980fe5d..39961879c414 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4754,19 +4754,14 @@ static int hp_blike_system(u32 subsystem_id); static void set_hp_led_gpio(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - switch (codec->vendor_id) { - case 0x111d7608: - /* GPIO 0 */ - spec->gpio_led = 0x01; - break; - case 0x111d7600: - case 0x111d7601: - case 0x111d7602: - case 0x111d7603: - /* GPIO 3 */ - spec->gpio_led = 0x08; - break; - } + unsigned int gpio; + + gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio &= AC_GPIO_IO_COUNT; + if (gpio > 3) + spec->gpio_led = 0x08; /* GPIO 3 */ + else + spec->gpio_led = 0x01; /* GPIO 0 */ } /* -- cgit v1.2.2 From c21bd0254371c207636e84c9e033d13a6fe48d43 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Feb 2010 15:16:08 +0100 Subject: ALSA: hda - Merge HP mute-LED status callback on both IDT 92HD7x and 8x codecs Merge the mute-LED status callback function for both IDT 92HD7x and 8x codecs to one function. Also it's changed to check all DACs, and called in the initialization to sync with the current status. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 57 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 30 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 39961879c414..ea254235470d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4363,6 +4363,12 @@ static int stac92xx_init(struct hda_codec *codec) if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) stac_issue_unsol_event(codec, nid); } + +#ifdef CONFIG_SND_HDA_POWER_SAVE + /* sync mute LED */ + if (spec->gpio_led && codec->patch_ops.check_power_status) + codec->patch_ops.check_power_status(codec, 0x01); +#endif if (spec->dac_list) stac92xx_power_down(codec); return 0; @@ -4909,6 +4915,11 @@ static int stac92xx_resume(struct hda_codec *codec) stac_issue_unsol_event(codec, spec->autocfg.line_out_pins[0]); } +#ifdef CONFIG_SND_HDA_POWER_SAVE + /* sync mute LED */ + if (spec->gpio_led && codec->patch_ops.check_power_status) + codec->patch_ops.check_power_status(codec, 0x01); +#endif return 0; } @@ -4928,43 +4939,29 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct sigmatel_spec *spec = codec->spec; + int i, muted = 1; - if (nid == 0x10) { - if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & - HDA_AMP_MUTE) - spec->gpio_data &= ~spec->gpio_led; /* orange */ - else - spec->gpio_data |= spec->gpio_led; /* white */ - - if (!spec->gpio_led_polarity) { - /* LED state is inverted on these systems */ - spec->gpio_data ^= spec->gpio_led; + for (i = 0; i < spec->multiout.num_dacs; i++) { + nid = spec->multiout.dac_nids[i]; + if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & + HDA_AMP_MUTE)) { + muted = 0; /* something heard */ + break; } - - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, - spec->gpio_data); } + if (muted) + spec->gpio_data &= ~spec->gpio_led; /* orange */ + else + spec->gpio_data |= spec->gpio_led; /* white */ - return 0; -} - -static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec, - hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; + if (!spec->gpio_led_polarity) { + /* LED state is inverted on these systems */ + spec->gpio_data ^= spec->gpio_led; + } - if (nid != 0x13) - return 0; - if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE) - spec->gpio_data |= spec->gpio_led; /* mute LED on */ - else - spec->gpio_data &= ~spec->gpio_led; /* mute LED off */ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); - return 0; } - #endif static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) @@ -5361,7 +5358,7 @@ again: spec->gpio_data |= spec->gpio_led; /* register check_power_status callback. */ codec->patch_ops.check_power_status = - idt92hd83xxx_hp_check_power_status; + stac92xx_hp_check_power_status; } #endif -- cgit v1.2.2 From b99a776d0b17ae0f3a54e86009887a00ac4889d0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Feb 2010 15:21:09 +0100 Subject: ALSA: hda - Remove static gpio_led setup via model We have now a better mute-LED GPIO detection, and no need to assign the values statically per model option. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ea254235470d..ec0637e7d488 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -5343,9 +5343,6 @@ again: codec->patch_ops = stac92xx_patch_ops; - if (spec->board_config == STAC_92HD83XXX_HP) - spec->gpio_led = 0x01; - if (find_mute_led_gpio(codec)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, @@ -5673,7 +5670,6 @@ again: */ spec->num_smuxes = 1; spec->num_dmuxes = 1; - spec->gpio_led = 0x01; /* fallthrough */ case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); @@ -5688,8 +5684,6 @@ again: spec->num_dmics = 1; spec->num_dmuxes = 1; spec->num_smuxes = 1; - /* orange/white mute led on GPIO3, orange=0, white=1 */ - spec->gpio_led = 0x08; break; } -- cgit v1.2.2 From dce17d4ff366230aeeaaf42512bba3711243cf1c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 9 Feb 2010 09:25:26 +0100 Subject: ALSA: hda - Fix default polarity of mute-LED GPIO on 92HD83x/88x codecs The previous commit caused a regression on HP laptops with 92HD83x/88x codecs. The default polarity of mute-LED GPIO is inverted on these devices. Reference: Novell bnc#578190 https://bugzilla.novell.com/show_bug.cgi?id=578190 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ec0637e7d488..8c416bb18a57 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4790,7 +4790,7 @@ static void set_hp_led_gpio(struct hda_codec *codec) * Need more information on whether it is true across the entire series. * -- kunal */ -static int find_mute_led_gpio(struct hda_codec *codec) +static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity) { struct sigmatel_spec *spec = codec->spec; const struct dmi_device *dev = NULL; @@ -4817,7 +4817,7 @@ static int find_mute_led_gpio(struct hda_codec *codec) */ if (!hp_blike_system(codec->subsystem_id)) { set_hp_led_gpio(codec); - spec->gpio_led_polarity = 1; + spec->gpio_led_polarity = default_polarity; return 1; } } @@ -5343,7 +5343,7 @@ again: codec->patch_ops = stac92xx_patch_ops; - if (find_mute_led_gpio(codec)) + if (find_mute_led_gpio(codec, 0)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, spec->gpio_led_polarity); @@ -5705,7 +5705,7 @@ again: } } - if (find_mute_led_gpio(codec)) + if (find_mute_led_gpio(codec, 1)) snd_printd("mute LED gpio %d polarity %d\n", spec->gpio_led, spec->gpio_led_polarity); -- cgit v1.2.2 From cebe41d4b8f8092359de31e241815fcb4b4dc0be Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 6 Feb 2010 00:21:03 +0200 Subject: sound: use DEFINE_PCI_DEVICE_TABLE Use DEFINE_PCI_DEVICE_TABLE() to make PCI device ids go to .devinit.rodata section, so they can be discarded in some cases, and make them const. Signed-off-by: Alexey Dobriyan Signed-off-by: Takashi Iwai --- sound/pci/ad1889.c | 2 +- sound/pci/ali5451/ali5451.c | 2 +- sound/pci/als300.c | 2 +- sound/pci/als4000.c | 2 +- sound/pci/atiixp.c | 2 +- sound/pci/atiixp_modem.c | 2 +- sound/pci/au88x0/au8810.c | 2 +- sound/pci/au88x0/au8820.c | 2 +- sound/pci/au88x0/au8830.c | 2 +- sound/pci/aw2/aw2-alsa.c | 2 +- sound/pci/azt3328.c | 2 +- sound/pci/bt87x.c | 4 ++-- sound/pci/ca0106/ca0106_main.c | 2 +- sound/pci/cmipci.c | 4 ++-- sound/pci/cs4281.c | 2 +- sound/pci/cs46xx/cs46xx.c | 2 +- sound/pci/cs5530.c | 2 +- sound/pci/cs5535audio/cs5535audio.c | 2 +- sound/pci/ctxfi/xfi.c | 2 +- sound/pci/echoaudio/darla20.c | 2 +- sound/pci/echoaudio/darla24.c | 2 +- sound/pci/echoaudio/echo3g.c | 2 +- sound/pci/echoaudio/gina20.c | 2 +- sound/pci/echoaudio/gina24.c | 2 +- sound/pci/echoaudio/indigo.c | 2 +- sound/pci/echoaudio/indigodj.c | 2 +- sound/pci/echoaudio/indigodjx.c | 2 +- sound/pci/echoaudio/indigoio.c | 2 +- sound/pci/echoaudio/indigoiox.c | 2 +- sound/pci/echoaudio/layla20.c | 2 +- sound/pci/echoaudio/layla24.c | 2 +- sound/pci/echoaudio/mia.c | 2 +- sound/pci/echoaudio/mona.c | 2 +- sound/pci/emu10k1/emu10k1.c | 2 +- sound/pci/emu10k1/emu10k1x.c | 2 +- sound/pci/ens1370.c | 2 +- sound/pci/es1938.c | 2 +- sound/pci/es1968.c | 2 +- sound/pci/fm801.c | 2 +- sound/pci/hda/hda_intel.c | 2 +- sound/pci/ice1712/ice1712.c | 2 +- sound/pci/ice1712/ice1724.c | 2 +- sound/pci/intel8x0.c | 2 +- sound/pci/intel8x0m.c | 2 +- sound/pci/korg1212/korg1212.c | 2 +- sound/pci/lx6464es/lx6464es.c | 2 +- sound/pci/maestro3.c | 2 +- sound/pci/mixart/mixart.c | 2 +- sound/pci/nm256/nm256.c | 2 +- sound/pci/oxygen/hifier.c | 2 +- sound/pci/oxygen/oxygen.c | 2 +- sound/pci/oxygen/virtuoso.c | 2 +- sound/pci/pcxhr/pcxhr.c | 2 +- sound/pci/riptide/riptide.c | 4 ++-- sound/pci/rme32.c | 2 +- sound/pci/rme96.c | 2 +- sound/pci/rme9652/hdsp.c | 2 +- sound/pci/rme9652/hdspm.c | 2 +- sound/pci/rme9652/rme9652.c | 2 +- sound/pci/sis7019.c | 2 +- sound/pci/sonicvibes.c | 2 +- sound/pci/trident/trident.c | 2 +- sound/pci/via82xx.c | 2 +- sound/pci/via82xx_modem.c | 2 +- sound/pci/vx222/vx222.c | 2 +- sound/pci/ymfpci/ymfpci.c | 2 +- 66 files changed, 69 insertions(+), 69 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 8f5098f92c37..4382d0fa6b9a 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -1048,7 +1048,7 @@ snd_ad1889_remove(struct pci_dev *pci) pci_set_drvdata(pci, NULL); } -static struct pci_device_id snd_ad1889_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) }, { 0, }, }; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index aaf4da68969c..5c6e322a48f0 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -275,7 +275,7 @@ struct snd_ali { #endif }; -static struct pci_device_id snd_ali_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_ali_ids) = { {PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0}, {0, } }; diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 3aa35af7ca91..d7653cb7ac60 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -145,7 +145,7 @@ struct snd_als300_substream_data { int block_counter_register; }; -static struct pci_device_id snd_als300_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_als300_ids) = { { 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 }, { 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS }, { 0, } diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 3dbacde1a5af..d75cf7b06426 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -117,7 +117,7 @@ struct snd_card_als4000 { #endif }; -static struct pci_device_id snd_als4000_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_als4000_ids) = { { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */ { 0, } }; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d6752dff2a44..81e2bfc11257 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -286,7 +286,7 @@ struct atiixp { /* */ -static struct pci_device_id snd_atiixp_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = { { PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */ { PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */ { PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */ diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index e7e147bf8eb2..91d7036b6411 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -261,7 +261,7 @@ struct atiixp_modem { /* */ -static struct pci_device_id snd_atiixp_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = { { PCI_VDEVICE(ATI, 0x434d), 0 }, /* SB200 */ { PCI_VDEVICE(ATI, 0x4378), 0 }, /* SB400 */ { 0, } diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c index c0e8c6b295cb..aa51cc7771dd 100644 --- a/sound/pci/au88x0/au8810.c +++ b/sound/pci/au88x0/au8810.c @@ -1,6 +1,6 @@ #include "au8810.h" #include "au88x0.h" -static struct pci_device_id snd_vortex_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = { {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,}, {0,} }; diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c index a6527330df58..2f321e7306cd 100644 --- a/sound/pci/au88x0/au8820.c +++ b/sound/pci/au88x0/au8820.c @@ -1,6 +1,6 @@ #include "au8820.h" #include "au88x0.h" -static struct pci_device_id snd_vortex_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = { {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,}, {0,} }; diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c index 6c702ad4352a..279b78f06d22 100644 --- a/sound/pci/au88x0/au8830.c +++ b/sound/pci/au88x0/au8830.c @@ -1,6 +1,6 @@ #include "au8830.h" #include "au88x0.h" -static struct pci_device_id snd_vortex_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = { {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,}, {0,} }; diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 4d34bb0d99d3..67921f93a41e 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -164,7 +164,7 @@ 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[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = { {PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, 0, 0, 0, 0, 0}, {0} diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 69867ace7860..4679ed83a43b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -350,7 +350,7 @@ struct snd_azf3328 { #endif }; -static const struct pci_device_id snd_azf3328_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = { { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */ { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */ { 0, } diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 4e2b925a94cc..37e1b5df5ab8 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -795,7 +795,7 @@ fail: .driver_data = SND_BT87X_BOARD_ ## id } /* driver_data is the card id for that device */ -static struct pci_device_id snd_bt87x_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_ids) = { /* Hauppauge WinTV series */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC), /* Hauppauge WinTV series */ @@ -964,7 +964,7 @@ static void __devexit snd_bt87x_remove(struct pci_dev *pci) /* default entries for all Bt87x cards - it's not exported */ /* driver_data is set to 0 to call detection */ -static struct pci_device_id snd_bt87x_default_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = { BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN), BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN), { } diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 15e4138bce17..0a3d3d6e77b4 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1875,7 +1875,7 @@ static int snd_ca0106_resume(struct pci_dev *pci) #endif // PCI IDs -static struct pci_device_id snd_ca0106_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = { { PCI_VDEVICE(CREATIVE, 0x0007), 0 }, /* Audigy LS or Live 24bit */ { 0, } }; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index a312bae08f52..1ded64e05643 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2796,7 +2796,7 @@ static inline void snd_cmipci_proc_init(struct cmipci *cm) {} #endif -static struct pci_device_id snd_cmipci_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = { {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0}, {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0}, {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0}, @@ -3018,7 +3018,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc int integrated_midi = 0; char modelstr[16]; int pcm_index, pcm_spdif_index; - static struct pci_device_id intel_82437vx[] = { + static DEFINE_PCI_DEVICE_TABLE(intel_82437vx) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) }, { }, }; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index e2e0359bb056..9edc65059e3e 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -494,7 +494,7 @@ struct cs4281 { static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); -static struct pci_device_id snd_cs4281_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_cs4281_ids) = { { PCI_VDEVICE(CIRRUS, 0x6005), 0, }, /* CS4281 */ { 0, } }; diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 033aec430117..767fa7f06cd0 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -64,7 +64,7 @@ MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); module_param_array(mmap_valid, bool, NULL, 0444); MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); -static struct pci_device_id snd_cs46xx_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = { { PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */ { PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */ { PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */ diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index dc464321d0f3..207479a641cf 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -58,7 +58,7 @@ struct snd_cs5530 { unsigned long pci_base; }; -static struct pci_device_id snd_cs5530_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = { {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0}, {0,} diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 91e7faf69bbb..afb803708416 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -66,7 +66,7 @@ MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME); -static struct pci_device_id snd_cs5535audio_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) }, {} diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index ed44ed788b60..f42e7e1a1074 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -43,7 +43,7 @@ MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver"); module_param_array(subsystem, int, NULL, 0444); MODULE_PARM_DESC(subsystem, "Override subsystem ID for Creative X-Fi driver"); -static struct pci_device_id ct_pci_dev_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = { /* only X-Fi is supported, so... */ { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1), .driver_data = ATC20K1, diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index 8c6db3aa3c1a..a65bafe0800f 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -63,7 +63,7 @@ static const struct firmware card_fw[] = { {0, "darla20_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */ {0,} }; diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index 04cbf3eaf05a..0a6c50bcd758 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -67,7 +67,7 @@ static const struct firmware card_fw[] = { {0, "darla24_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */ {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */ {0,} diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index 4022e43a0053..f5142796989b 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -81,7 +81,7 @@ static const struct firmware card_fw[] = { {0, "3g_asic.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */ {0,} }; diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index c0e64b8f52a4..2364f8a1bc21 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -67,7 +67,7 @@ static const struct firmware card_fw[] = { {0, "gina20_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */ {0,} }; diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index c36a78dd0b5e..616b55825a19 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -85,7 +85,7 @@ static const struct firmware card_fw[] = { {0, "gina24_361_asic.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */ {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */ {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */ diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 0a58a7c1fd7c..776175c0bdad 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -68,7 +68,7 @@ static const struct firmware card_fw[] = { {0, "indigo_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */ {0,} }; diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 2db24d29332b..8816b0bd2ba6 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -68,7 +68,7 @@ static const struct firmware card_fw[] = { {0, "indigo_dj_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/ {0,} }; diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c index 2e44316530a2..b1e3652f2f48 100644 --- a/sound/pci/echoaudio/indigodjx.c +++ b/sound/pci/echoaudio/indigodjx.c @@ -68,7 +68,7 @@ static const struct firmware card_fw[] = { {0, "indigo_djx_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x00E0, 0, 0, 0}, /* Indigo DJx*/ {0,} }; diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index a60c0a0a89b7..1035125336d6 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -69,7 +69,7 @@ static const struct firmware card_fw[] = { {0, "indigo_io_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/ {0,} }; diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c index eb3819f9654a..60b7cb2753cf 100644 --- a/sound/pci/echoaudio/indigoiox.c +++ b/sound/pci/echoaudio/indigoiox.c @@ -69,7 +69,7 @@ static const struct firmware card_fw[] = { {0, "indigo_iox_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x00D0, 0, 0, 0}, /* Indigo IOx */ {0,} }; diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 506194688995..8c3f5c5b5301 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -76,7 +76,7 @@ static const struct firmware card_fw[] = { {0, "layla20_asic.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */ {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */ {0,} diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index e09e3ea7781e..ed1cc0abc2b8 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -87,7 +87,7 @@ static const struct firmware card_fw[] = { {0, "layla24_2S_asic.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */ {0,} }; diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index f05c8c097aa8..cc2bbfc65327 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -77,7 +77,7 @@ static const struct firmware card_fw[] = { {0, "mia_dsp.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */ {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */ {0,} diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index b05bad944901..3e7e01824b40 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -92,7 +92,7 @@ static const struct firmware card_fw[] = { {0, "mona_2_asic.fw"} }; -static struct pci_device_id snd_echo_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = { {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */ {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */ {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */ diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 168af67d938e..4203782d7cb7 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -76,7 +76,7 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model."); /* * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400 */ -static struct pci_device_id snd_emu10k1_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1_ids) = { { PCI_VDEVICE(CREATIVE, 0x0002), 0 }, /* EMU10K1 */ { PCI_VDEVICE(CREATIVE, 0x0004), 1 }, /* Audigy */ { PCI_VDEVICE(CREATIVE, 0x0008), 1 }, /* Audigy 2 Value SB0400 */ diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 1d369ff73805..df47f738098d 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1605,7 +1605,7 @@ static void __devexit snd_emu10k1x_remove(struct pci_dev *pci) } // PCI IDs -static struct pci_device_id snd_emu10k1x_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = { { PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */ { 0, } }; diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 2b82c5c723e1..c7fba5379813 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -443,7 +443,7 @@ struct ensoniq { static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id); -static struct pci_device_id snd_audiopci_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_audiopci_ids) = { #ifdef CHIP1370 { PCI_VDEVICE(ENSONIQ, 0x5000), 0, }, /* ES1370 */ #endif diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fb83e1ffa5cb..553b75217259 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -243,7 +243,7 @@ struct es1938 { static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id); -static struct pci_device_id snd_es1938_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_es1938_ids) = { { PCI_VDEVICE(ESS, 0x1969), 0, }, /* Solo-1 */ { 0, } }; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index a11f453a6b6d..ecaea9fb48ec 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -551,7 +551,7 @@ struct es1968 { static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id); -static struct pci_device_id snd_es1968_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_es1968_ids) = { /* Maestro 1 */ { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO }, /* Maestro 2 */ diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 83508b3964fb..e1baad74ea4b 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -205,7 +205,7 @@ struct fm801 { #endif }; -static struct pci_device_id snd_fm801_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = { { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */ { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */ { 0, } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1f516e668d88..ac05bef7c2ec 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2664,7 +2664,7 @@ static void __devexit azx_remove(struct pci_dev *pci) } /* PCI IDs */ -static struct pci_device_id azx_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* ICH 6..10 */ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH }, { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH }, diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index fb61943fc4dc..4fc6d8bc637e 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -106,7 +106,7 @@ module_param_array(dxr_enable, int, NULL, 0444); MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE."); -static const struct pci_device_id snd_ice1712_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_ice1712_ids) = { { PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 }, /* ICE1712 */ { 0, } }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index ae29073eea93..c1498fa5545f 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -94,7 +94,7 @@ MODULE_PARM_DESC(model, "Use the given board model."); /* Both VT1720 and VT1724 have the same PCI IDs */ -static const struct pci_device_id snd_vt1724_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_vt1724_ids) = { { PCI_VDEVICE(ICE, PCI_DEVICE_ID_VT1724), 0 }, { 0, } }; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index b990143636f1..6433e65c9507 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -420,7 +420,7 @@ struct intel8x0 { u32 int_sta_mask; /* interrupt status mask */ }; -static struct pci_device_id snd_intel8x0_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0_ids) = { { PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL }, /* 82801AA */ { PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL }, /* 82901AB */ { PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL }, /* 82801BA */ diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 9e7d12e7673f..13cec1e5ced9 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -219,7 +219,7 @@ struct intel8x0m { unsigned int pcm_pos_shift; }; -static struct pci_device_id snd_intel8x0m_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = { { PCI_VDEVICE(INTEL, 0x2416), DEVICE_INTEL }, /* 82801AA */ { PCI_VDEVICE(INTEL, 0x2426), DEVICE_INTEL }, /* 82901AB */ { PCI_VDEVICE(INTEL, 0x2446), DEVICE_INTEL }, /* 82801BA */ diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 7cc38a11e997..6d795700be79 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -418,7 +418,7 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard."); MODULE_AUTHOR("Haroldo Gamal "); -static struct pci_device_id snd_korg1212_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = { { .vendor = 0x10b5, .device = 0x906d, diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 11b8c6514b3d..0cca56038cd9 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -55,7 +55,7 @@ static const char card_name[] = "LX6464ES"; #define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056 -static struct pci_device_id snd_lx6464es_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_lx6464es_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), .subvendor = PCI_VENDOR_ID_DIGIGRAM, .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 75283fbb4b3f..b64e78139d63 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -861,7 +861,7 @@ struct snd_m3 { /* * pci ids */ -static struct pci_device_id snd_m3_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_m3_ids) = { {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID, diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index a83d1968a845..7e8e7da592a9 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -60,7 +60,7 @@ MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); /* */ -static struct pci_device_id snd_mixart_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_mixart_ids) = { { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */ { 0, } }; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 97a0731331a1..5a60492ac7b3 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -262,7 +262,7 @@ struct nm256 { /* * PCI ids */ -static struct pci_device_id snd_nm256_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_nm256_ids) = { {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0}, {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0}, {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0}, diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index e3c229b63311..5a87d683691f 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -48,7 +48,7 @@ MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable card"); -static struct pci_device_id hifier_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = { { OXYGEN_PCI_SUBID(0x14c3, 0x1710) }, { OXYGEN_PCI_SUBID(0x14c3, 0x1711) }, { OXYGEN_PCI_SUBID_BROKEN_EEPROM }, diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index acbedebcffd9..289cb4dacfc7 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -72,7 +72,7 @@ enum { MODEL_CLARO_HALO, /* HT-Omega Claro halo */ }; -static struct pci_device_id oxygen_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = { { OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF }, { OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF }, diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 563b6f50821f..f03a2f2cffee 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -40,7 +40,7 @@ MODULE_PARM_DESC(id, "ID string"); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "enable card"); -static struct pci_device_id xonar_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = { { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, { OXYGEN_PCI_SUBID(0x1043, 0x8275) }, { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 833e9c7b27c7..95cfde27d25c 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -94,7 +94,7 @@ enum { PCI_ID_LAST }; -static struct pci_device_id pcxhr_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(pcxhr_ids) = { { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, }, { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, }, { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, }, diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index b5ca02e2038c..bb08a2855fce 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -506,7 +506,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip); /* */ -static struct pci_device_id snd_riptide_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_riptide_ids) = { { PCI_DEVICE(0x127a, 0x4310) }, { PCI_DEVICE(0x127a, 0x4320) }, { PCI_DEVICE(0x127a, 0x4330) }, @@ -515,7 +515,7 @@ static struct pci_device_id snd_riptide_ids[] = { }; #ifdef SUPPORT_JOYSTICK -static struct pci_device_id snd_riptide_joystick_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(snd_riptide_joystick_ids) = { { PCI_DEVICE(0x127a, 0x4312) }, { PCI_DEVICE(0x127a, 0x4322) }, { PCI_DEVICE(0x127a, 0x4332) }, diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index f977dba7cbd0..d5e1c6eb7b7b 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -226,7 +226,7 @@ struct rme32 { struct snd_kcontrol *spdif_ctl; }; -static struct pci_device_id snd_rme32_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_rme32_ids) = { {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32), 0,}, {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8), 0,}, {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_PRO), 0,}, diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2ba5c0fd55db..9d5252bc870c 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -231,7 +231,7 @@ struct rme96 { struct snd_kcontrol *spdif_ctl; }; -static struct pci_device_id snd_rme96_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_rme96_ids) = { { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96), 0, }, { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8), 0, }, { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO), 0, }, diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 7bb827c7d806..52c6eb57cc3f 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -585,7 +585,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d } -static struct pci_device_id snd_hdsp_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_hdsp_ids) = { { .vendor = PCI_VENDOR_ID_XILINX, .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a1b10d1a384d..3d72c1effeef 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -512,7 +512,7 @@ static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = { }; -static struct pci_device_id snd_hdspm_ids[] __devinitdata = { +static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = { { .vendor = PCI_VENDOR_ID_XILINX, .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI, diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index bc539abb2105..44a3e2d8c556 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -314,7 +314,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d } -static struct pci_device_id snd_rme9652_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_rme9652_ids) = { { .vendor = 0x10ee, .device = 0x3fc4, diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 1a5ff0611072..7e3e8fbc90fe 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -48,7 +48,7 @@ MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator."); module_param(enable, bool, 0444); MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator."); -static struct pci_device_id snd_sis7019_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_sis7019_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) }, { 0, } }; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 1f6406c4534d..337b9facadfd 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -242,7 +242,7 @@ struct sonicvibes { #endif }; -static struct pci_device_id snd_sonic_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_sonic_ids) = { { PCI_VDEVICE(S3, 0xca00), 0, }, { 0, } }; diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 21cef97d478d..6d0581841d7a 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM."); module_param_array(wavetable_size, int, NULL, 0444); MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth."); -static struct pci_device_id snd_trident_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_trident_ids) = { {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX), PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0}, {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX), diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8a332d2f615c..9595b5b535f3 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -401,7 +401,7 @@ struct via82xx { #endif }; -static struct pci_device_id snd_via82xx_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_ids) = { /* 0x1106, 0x3058 */ { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C686_5), TYPE_CARD_VIA686, }, /* 686A */ /* 0x1106, 0x3059 */ diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 47eb61561dfc..f7e8bbbe3953 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -260,7 +260,7 @@ struct via82xx_modem { struct snd_info_entry *proc_entry; }; -static struct pci_device_id snd_via82xx_modem_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_modem_ids) = { { PCI_VDEVICE(VIA, 0x3068), TYPE_CARD_VIA82XX_MODEM, }, { 0, } }; diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index fc9136c3e0d7..99a9a814be0b 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -60,7 +60,7 @@ enum { VX_PCI_VX222_NEW }; -static struct pci_device_id snd_vx222_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_vx222_ids) = { { 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, }, /* PLX */ { 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, }, /* PLX */ { 0, } diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index e6b18b90d451..80c682113381 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -66,7 +66,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address"); module_param_array(rear_switch, bool, NULL, 0444); MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch"); -static struct pci_device_id snd_ymfpci_ids[] = { +static DEFINE_PCI_DEVICE_TABLE(snd_ymfpci_ids) = { { PCI_VDEVICE(YAMAHA, 0x0004), 0, }, /* YMF724 */ { PCI_VDEVICE(YAMAHA, 0x000d), 0, }, /* YMF724F */ { PCI_VDEVICE(YAMAHA, 0x000a), 0, }, /* YMF740 */ -- cgit v1.2.2 From 3ad2f3fbb961429d2aa627465ae4829758bc7e07 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 3 Feb 2010 08:01:28 +0800 Subject: tree-wide: Assorted spelling fixes In particular, several occurances of funny versions of 'success', 'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address', 'beginning', 'desirable', 'separate' and 'necessary' are fixed. Signed-off-by: Daniel Mack Cc: Joe Perches Cc: Junio C Hamano Signed-off-by: Jiri Kosina --- sound/pci/rme9652/hdspm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a1b10d1a384d..db0ed1cbd982 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2479,7 +2479,7 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, on MADICARD - playback mixer matrix: [channelout+64] [output] [value] - input(thru) mixer matrix: [channelin] [output] [value] - (better do 2 kontrols for seperation ?) + (better do 2 kontrols for separation ?) */ #define HDSPM_MIXER(xname, xindex) \ -- cgit v1.2.2 From fed08d036f2aabd8d0c684439de37f8ebec2bbc2 Mon Sep 17 00:00:00 2001 From: Jody Bruchon Date: Sat, 6 Feb 2010 10:46:26 -0500 Subject: ALSA: hda-intel: Avoid divide by zero crash On my AMD780V chipset, hda_intel.c can crash the kernel with a divide by zero for as-yet unknown reasons. A simple check for zero prevents it, though the problem that causes it remains. Since the workaround is harmless and won't affect anyone except victims of this bug, it should be safe; moreover, because this crash can be triggered by a user-mode application, there are denial of service implications on the systems affected by the bug without the patch. Signed-off-by: Jody Bruchon Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b8faa6dc5abe..e767c3f395ab 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1893,6 +1893,12 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) if (!bdl_pos_adj[chip->dev_index]) return 1; /* no delayed ack */ + if (azx_dev->period_bytes == 0) { + printk(KERN_WARNING + "hda-intel: Divide by zero was avoided " + "in azx_dev->period_bytes.\n"); + return 0; + } if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) return 0; /* NG - it's below the period boundary */ return 1; /* OK, it's fine */ -- cgit v1.2.2 From d6d8bf549393484e906913f02fa3c9518a2819b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 12 Feb 2010 18:17:06 +0100 Subject: ALSA: hda - use WARN_ON_ONCE() for zero-division detection Replace the zero-division warning message with WARN_ON_ONCE() per the advice by Linus. This shouldn't happen, but if it happens, it's possible that the bug happens often due to buggy IRQs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e767c3f395ab..3600e9cc9bc6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1893,12 +1893,9 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) if (!bdl_pos_adj[chip->dev_index]) return 1; /* no delayed ack */ - if (azx_dev->period_bytes == 0) { - printk(KERN_WARNING - "hda-intel: Divide by zero was avoided " - "in azx_dev->period_bytes.\n"); - return 0; - } + if (WARN_ONCE(!azx_dev->period_bytes, + "hda-intel: zero azx_dev->period_bytes")) + return 0; /* this shouldn't happen! */ if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) return 0; /* NG - it's below the period boundary */ return 1; /* OK, it's fine */ -- cgit v1.2.2 From cfd3d8dcf7b4fc783db0806ac3936a7b44735bf7 Mon Sep 17 00:00:00 2001 From: Greg Alexander Date: Sat, 13 Feb 2010 02:02:25 -0500 Subject: ALSA: hda - Add support for Lenovo IdeaPad U150 Add patch for the Conexant 5066 HDA codec to support the Lenovo IdeaPad U150 Signed-off-by: Greg Alexander Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 130 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 745e35992144..194a28c54992 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -113,7 +113,8 @@ struct conexant_spec { unsigned int dell_automute; unsigned int port_d_mode; - unsigned int dell_vostro; + unsigned int dell_vostro:1; + unsigned int ideapad:1; unsigned int ext_mic_present; unsigned int recording; @@ -2167,6 +2168,34 @@ static void cxt5066_vostro_automic(struct hda_codec *codec) } } +/* toggle input of built-in digital mic and mic jack appropriately */ +static void cxt5066_ideapad_automic(struct hda_codec *codec) +{ + unsigned int present; + + struct hda_verb ext_mic_present[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb ext_mic_absent[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + + present = snd_hda_jack_detect(codec, 0x1b); + if (present) { + snd_printdd("CXT5066: external microphone detected\n"); + snd_hda_sequence_write(codec, ext_mic_present); + } else { + snd_printdd("CXT5066: external microphone absent\n"); + snd_hda_sequence_write(codec, ext_mic_absent); + } +} + /* mute internal speaker if HP is plugged */ static void cxt5066_hp_automute(struct hda_codec *codec) { @@ -2216,6 +2245,20 @@ static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res) } } +/* unsolicited event for jack sensing */ +static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_ideapad_automic(codec); + break; + } +} + static const struct hda_input_mux cxt5066_analog_mic_boost = { .num_items = 5, .items = { @@ -2227,13 +2270,21 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = { }, }; -static int cxt5066_set_mic_boost(struct hda_codec *codec) +static void cxt5066_set_mic_boost(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - return snd_hda_codec_write_cache(codec, 0x17, 0, + snd_hda_codec_write_cache(codec, 0x17, 0, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | cxt5066_analog_mic_boost.items[spec->mic_boost].index); + if (spec->ideapad) { + /* adjust the internal mic as well...it is not through 0x17 */ + snd_hda_codec_write_cache(codec, 0x23, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT | + cxt5066_analog_mic_boost. + items[spec->mic_boost].index); + } } static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol, @@ -2664,6 +2715,56 @@ static struct hda_verb cxt5066_init_verbs_vostro[] = { { } /* end */ }; +static struct hda_verb cxt5066_init_verbs_ideapad[] = { + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */ + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ + + /* Speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* HP, Amp */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */ + + /* Audio input selector */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2}, + {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */ + + /* SPDIF route: PCM */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, + + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* internal microphone */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */ + + /* EAPD */ + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + static struct hda_verb cxt5066_init_verbs_portd_lo[] = { {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, { } /* end */ @@ -2680,6 +2781,8 @@ static int cxt5066_init(struct hda_codec *codec) cxt5066_hp_automute(codec); if (spec->dell_vostro) cxt5066_vostro_automic(codec); + else if (spec->ideapad) + cxt5066_ideapad_automic(codec); } cxt5066_set_mic_boost(codec); return 0; @@ -2705,6 +2808,7 @@ enum { CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ + CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ CXT5066_MODELS }; @@ -2712,7 +2816,8 @@ static const char *cxt5066_models[CXT5066_MODELS] = { [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", - [CXT5066_DELL_VOSTO] = "dell-vostro" + [CXT5066_DELL_VOSTO] = "dell-vostro", + [CXT5066_IDEAPAD] = "ideapad", }; static struct snd_pci_quirk cxt5066_cfg_tbl[] = { @@ -2722,6 +2827,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} }; @@ -2810,6 +2916,22 @@ static int patch_cxt5066(struct hda_codec *codec) /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ + spec->input_mux = NULL; + break; + case CXT5066_IDEAPAD: + codec->patch_ops.init = cxt5066_init; + codec->patch_ops.unsol_event = cxt5066_ideapad_event; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->init_verbs[0] = cxt5066_init_verbs_ideapad; + spec->port_d_mode = 0; + spec->ideapad = 1; + spec->mic_boost = 2; /* default 20dB gain */ + + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ spec->input_mux = NULL; break; -- cgit v1.2.2 From 19b50063780953563e3c3a2867c39aad7b9e64cf Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Sun, 14 Feb 2010 18:15:34 +0100 Subject: ALSA: Echoaudio - Add firmware cache #1 Changes the way the firmware is passed through functions. When CONFIG_PM is enabled the firmware cannot be released because the driver will need it again to resume the card. With this patch the firmware is passed as an index of the struct firmware card_fw[] in place of a pointer. That same index is then used to locate the firmware in the firmware cache. Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/darla20_dsp.c | 2 +- sound/pci/echoaudio/darla24_dsp.c | 2 +- sound/pci/echoaudio/echo3g_dsp.c | 2 +- sound/pci/echoaudio/echoaudio.c | 8 +++++++- sound/pci/echoaudio/echoaudio.h | 6 +++--- sound/pci/echoaudio/echoaudio_3g.c | 5 ++--- sound/pci/echoaudio/echoaudio_dsp.c | 12 +++++++----- sound/pci/echoaudio/gina20_dsp.c | 2 +- sound/pci/echoaudio/gina24_dsp.c | 18 ++++++++--------- sound/pci/echoaudio/indigo_dsp.c | 2 +- sound/pci/echoaudio/indigodj_dsp.c | 2 +- sound/pci/echoaudio/indigodjx_dsp.c | 2 +- sound/pci/echoaudio/indigoio_dsp.c | 2 +- sound/pci/echoaudio/indigoiox_dsp.c | 2 +- sound/pci/echoaudio/layla20_dsp.c | 7 +++---- sound/pci/echoaudio/layla24_dsp.c | 19 +++++++++--------- sound/pci/echoaudio/mia_dsp.c | 2 +- sound/pci/echoaudio/mona_dsp.c | 39 ++++++++++++++++++------------------- 18 files changed, 69 insertions(+), 65 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index 29043301ebb8..a44135d6acbb 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -45,7 +45,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP]; + chip->dsp_code_to_load = FW_DARLA20_DSP; chip->spdif_status = GD_SPDIF_STATUS_UNDEF; chip->clock_state = GD_CLOCK_UNDEF; /* Since this card has no ASIC, mark it as loaded so everything diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index 60228731841f..d681da180829 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -45,7 +45,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP]; + chip->dsp_code_to_load = FW_DARLA24_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index 57967e580571..f0071935c0cb 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -61,7 +61,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; chip->has_midi = TRUE; - chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP]; + chip->dsp_code_to_load = FW_ECHO3G_DSP; /* Load the DSP code and the ASIC on the PCI card and get what type of external box is attached */ diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 1305f7ca02c3..78fc2637bfa6 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -36,11 +36,15 @@ MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1); + + static int get_firmware(const struct firmware **fw_entry, - const struct firmware *frm, struct echoaudio *chip) + struct echoaudio *chip, const short fw_index) { int err; char name[30]; + const struct firmware *frm = &card_fw[fw_index]; + DE_ACT(("firmware requested: %s\n", frm->data)); snprintf(name, sizeof(name), "ea/%s", frm->data); if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0) @@ -48,6 +52,8 @@ static int get_firmware(const struct firmware **fw_entry, return err; } + + static void free_firmware(const struct firmware *fw_entry) { release_firmware(fw_entry); diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index f9490ae36c2e..be76ef3b829a 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -442,8 +442,8 @@ struct echoaudio { u16 device_id, subdevice_id; u16 *dsp_code; /* Current DSP code loaded, * NULL if nothing loaded */ - const struct firmware *dsp_code_to_load;/* DSP code to load */ - const struct firmware *asic_code; /* Current ASIC code */ + short dsp_code_to_load; /* DSP code to load */ + short asic_code; /* Current ASIC code */ u32 comm_page_phys; /* Physical address of the * memory seen by DSP */ volatile u32 __iomem *dsp_registers; /* DSP's register base */ @@ -464,7 +464,7 @@ static int load_firmware(struct echoaudio *chip); static int wait_handshake(struct echoaudio *chip); static int send_vector(struct echoaudio *chip, u32 command); static int get_firmware(const struct firmware **fw_entry, - const struct firmware *frm, struct echoaudio *chip); + struct echoaudio *chip, const short fw_index); static void free_firmware(const struct firmware *fw_entry); #ifdef ECHOCARD_HAS_MIDI diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index e32a74897921..658db44ef746 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -227,12 +227,11 @@ static int load_asic(struct echoaudio *chip) /* Give the DSP a few milliseconds to settle down */ mdelay(2); - err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC, - &card_fw[FW_3G_ASIC]); + err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC, FW_3G_ASIC); if (err < 0) return err; - chip->asic_code = &card_fw[FW_3G_ASIC]; + chip->asic_code = FW_3G_ASIC; /* Now give the new ASIC some time to set up */ msleep(1000); diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 4df51ef5e095..031ef7e9da91 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -175,15 +175,15 @@ static inline int check_asic_status(struct echoaudio *chip) #ifdef ECHOCARD_HAS_ASIC /* Load ASIC code - done after the DSP is loaded */ -static int load_asic_generic(struct echoaudio *chip, u32 cmd, - const struct firmware *asic) +static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) { const struct firmware *fw; int err; u32 i, size; u8 *code; - if ((err = get_firmware(&fw, asic, chip)) < 0) { + err = get_firmware(&fw, chip, asic); + if (err < 0) { snd_printk(KERN_WARNING "Firmware not found !\n"); return err; } @@ -245,7 +245,8 @@ static int install_resident_loader(struct echoaudio *chip) return 0; } - if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) { + i = get_firmware(&fw, chip, FW_361_LOADER); + if (i < 0) { snd_printk(KERN_WARNING "Firmware not found !\n"); return i; } @@ -485,7 +486,8 @@ static int load_firmware(struct echoaudio *chip) chip->dsp_code = NULL; } - if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0) + err = get_firmware(&fw, chip, chip->dsp_code_to_load); + if (err < 0) return err; err = load_dsp(chip, (u16 *)fw->data); free_firmware(fw); diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index 3f1e7475faea..c5de88b6792d 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -49,7 +49,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP]; + chip->dsp_code_to_load = FW_GINA20_DSP; chip->spdif_status = GD_SPDIF_STATUS_UNDEF; chip->clock_state = GD_CLOCK_UNDEF; /* Since this card has no ASIC, mark it as loaded so everything diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 2fef37a2a5b9..093dd7ba0e81 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -33,8 +33,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force); static int set_input_clock(struct echoaudio *chip, u16 clock); static int set_professional_spdif(struct echoaudio *chip, char prof); static int set_digital_mode(struct echoaudio *chip, u8 mode); -static int load_asic_generic(struct echoaudio *chip, u32 cmd, - const struct firmware *asic); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); static int check_asic_status(struct echoaudio *chip); @@ -64,13 +63,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) /* Gina24 comes in both '301 and '361 flavors */ if (chip->device_id == DEVICE_ID_56361) { - chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP]; + chip->dsp_code_to_load = FW_GINA24_361_DSP; chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; } else { - chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP]; + chip->dsp_code_to_load = FW_GINA24_301_DSP; chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | @@ -125,7 +124,7 @@ static int load_asic(struct echoaudio *chip) { u32 control_reg; int err; - const struct firmware *fw; + short asic; if (chip->asic_loaded) return 1; @@ -135,14 +134,15 @@ static int load_asic(struct echoaudio *chip) /* Pick the correct ASIC for '301 or '361 Gina24 */ if (chip->device_id == DEVICE_ID_56361) - fw = &card_fw[FW_GINA24_361_ASIC]; + asic = FW_GINA24_361_ASIC; else - fw = &card_fw[FW_GINA24_301_ASIC]; + asic = FW_GINA24_301_ASIC; - if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0) + err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, asic); + if (err < 0) return err; - chip->asic_code = fw; + chip->asic_code = asic; /* Now give the new ASIC a little time to set up */ mdelay(10); diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index 0b2cd9c86277..8799d2e6536a 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP]; + chip->dsp_code_to_load = FW_INDIGO_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 08392916691e..cb1c92ca9fef 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP]; + chip->dsp_code_to_load = FW_INDIGO_DJ_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index f591fc2ed960..91dbfeb586a7 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -48,7 +48,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJX_DSP]; + chip->dsp_code_to_load = FW_INDIGO_DJX_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index 0604c8a85223..134e783d3486 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP]; + chip->dsp_code_to_load = FW_INDIGO_IO_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index f357521c79e6..766cf501799d 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -48,7 +48,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_INDIGO_IOX_DSP]; + chip->dsp_code_to_load = FW_INDIGO_IOX_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 83750e9fd7b4..07f32454757e 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -31,8 +31,7 @@ static int read_dsp(struct echoaudio *chip, u32 *data); static int set_professional_spdif(struct echoaudio *chip, char prof); -static int load_asic_generic(struct echoaudio *chip, u32 cmd, - const struct firmware *asic); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); static int check_asic_status(struct echoaudio *chip); static int update_flags(struct echoaudio *chip); @@ -54,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; chip->has_midi = TRUE; - chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP]; + chip->dsp_code_to_load = FW_LAYLA20_DSP; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; @@ -144,7 +143,7 @@ static int load_asic(struct echoaudio *chip) return 0; err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC, - &card_fw[FW_LAYLA20_ASIC]); + FW_LAYLA20_ASIC); if (err < 0) return err; diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index d61b5cbcccad..12dc00adca9f 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -32,8 +32,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force); static int set_input_clock(struct echoaudio *chip, u16 clock); static int set_professional_spdif(struct echoaudio *chip, char prof); static int set_digital_mode(struct echoaudio *chip, u8 mode); -static int load_asic_generic(struct echoaudio *chip, u32 cmd, - const struct firmware *asic); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); static int check_asic_status(struct echoaudio *chip); @@ -54,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; chip->has_midi = TRUE; - chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP]; + chip->dsp_code_to_load = FW_LAYLA24_DSP; chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; @@ -123,18 +122,18 @@ static int load_asic(struct echoaudio *chip) /* Load the ASIC for the PCI card */ err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC, - &card_fw[FW_LAYLA24_1_ASIC]); + FW_LAYLA24_1_ASIC); if (err < 0) return err; - chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC]; + chip->asic_code = FW_LAYLA24_2S_ASIC; /* Now give the new ASIC a little time to set up */ mdelay(10); /* Do the external one */ err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, - &card_fw[FW_LAYLA24_2S_ASIC]); + FW_LAYLA24_2S_ASIC); if (err < 0) return FALSE; @@ -299,7 +298,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) /* Depending on what digital mode you want, Layla24 needs different ASICs loaded. This function checks the ASIC needed for the new mode and sees if it matches the one already loaded. */ -static int switch_asic(struct echoaudio *chip, const struct firmware *asic) +static int switch_asic(struct echoaudio *chip, short asic) { s8 *monitors; @@ -335,7 +334,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) { u32 control_reg; int err, incompatible_clock; - const struct firmware *asic; + short asic; /* Set clock to "internal" if it's not compatible with the new mode */ incompatible_clock = FALSE; @@ -344,12 +343,12 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) case DIGITAL_MODE_SPDIF_RCA: if (chip->input_clock == ECHO_CLOCK_ADAT) incompatible_clock = TRUE; - asic = &card_fw[FW_LAYLA24_2S_ASIC]; + asic = FW_LAYLA24_2S_ASIC; break; case DIGITAL_MODE_ADAT: if (chip->input_clock == ECHO_CLOCK_SPDIF) incompatible_clock = TRUE; - asic = &card_fw[FW_LAYLA24_2A_ASIC]; + asic = FW_LAYLA24_2A_ASIC; break; default: DE_ACT(("Digital mode not supported: %d\n", mode)); diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index 551405114cbc..d0302f2f00db 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -53,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->device_id = device_id; chip->subdevice_id = subdevice_id; chip->bad_board = TRUE; - chip->dsp_code_to_load = &card_fw[FW_MIA_DSP]; + chip->dsp_code_to_load = FW_MIA_DSP; /* Since this card has no ASIC, mark it as loaded so everything works OK */ chip->asic_loaded = TRUE; diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index eaa619bd2a03..b28b8e4703cf 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -33,8 +33,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force); static int set_input_clock(struct echoaudio *chip, u16 clock); static int set_professional_spdif(struct echoaudio *chip, char prof); static int set_digital_mode(struct echoaudio *chip, u8 mode); -static int load_asic_generic(struct echoaudio *chip, u32 cmd, - const struct firmware *asic); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic); static int check_asic_status(struct echoaudio *chip); @@ -64,9 +63,9 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) /* Mona comes in both '301 and '361 flavors */ if (chip->device_id == DEVICE_ID_56361) - chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP]; + chip->dsp_code_to_load = FW_MONA_361_DSP; else - chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP]; + chip->dsp_code_to_load = FW_MONA_301_DSP; chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; chip->professional_spdif = FALSE; @@ -120,7 +119,7 @@ static int load_asic(struct echoaudio *chip) { u32 control_reg; int err; - const struct firmware *asic; + short asic; if (chip->asic_loaded) return 0; @@ -128,9 +127,9 @@ static int load_asic(struct echoaudio *chip) mdelay(10); if (chip->device_id == DEVICE_ID_56361) - asic = &card_fw[FW_MONA_361_1_ASIC48]; + asic = FW_MONA_361_1_ASIC48; else - asic = &card_fw[FW_MONA_301_1_ASIC48]; + asic = FW_MONA_301_1_ASIC48; err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic); if (err < 0) @@ -141,7 +140,7 @@ static int load_asic(struct echoaudio *chip) /* Do the external one */ err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC, - &card_fw[FW_MONA_2_ASIC]); + FW_MONA_2_ASIC); if (err < 0) return err; @@ -165,22 +164,22 @@ loaded. This function checks the ASIC needed for the new mode and sees if it matches the one already loaded. */ static int switch_asic(struct echoaudio *chip, char double_speed) { - const struct firmware *asic; int err; + short asic; /* Check the clock detect bits to see if this is a single-speed clock or a double-speed clock; load a new ASIC if necessary. */ if (chip->device_id == DEVICE_ID_56361) { if (double_speed) - asic = &card_fw[FW_MONA_361_1_ASIC96]; + asic = FW_MONA_361_1_ASIC96; else - asic = &card_fw[FW_MONA_361_1_ASIC48]; + asic = FW_MONA_361_1_ASIC48; } else { if (double_speed) - asic = &card_fw[FW_MONA_301_1_ASIC96]; + asic = FW_MONA_301_1_ASIC96; else - asic = &card_fw[FW_MONA_301_1_ASIC48]; + asic = FW_MONA_301_1_ASIC48; } if (asic != chip->asic_code) { @@ -200,7 +199,7 @@ static int switch_asic(struct echoaudio *chip, char double_speed) static int set_sample_rate(struct echoaudio *chip, u32 rate) { u32 control_reg, clock; - const struct firmware *asic; + short asic; char force_write; /* Only set the clock for internal mode. */ @@ -218,14 +217,14 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EINVAL; if (chip->device_id == DEVICE_ID_56361) - asic = &card_fw[FW_MONA_361_1_ASIC96]; + asic = FW_MONA_361_1_ASIC96; else - asic = &card_fw[FW_MONA_301_1_ASIC96]; + asic = FW_MONA_301_1_ASIC96; } else { if (chip->device_id == DEVICE_ID_56361) - asic = &card_fw[FW_MONA_361_1_ASIC48]; + asic = FW_MONA_361_1_ASIC48; else - asic = &card_fw[FW_MONA_301_1_ASIC48]; + asic = FW_MONA_301_1_ASIC48; } force_write = 0; @@ -410,8 +409,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) case DIGITAL_MODE_ADAT: /* If the current ASIC is the 96KHz ASIC, switch the ASIC and set to 48 KHz */ - if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] || - chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) { + if (chip->asic_code == FW_MONA_361_1_ASIC96 || + chip->asic_code == FW_MONA_301_1_ASIC96) { set_sample_rate(chip, 48000); } control_reg |= GML_ADAT_MODE; -- cgit v1.2.2 From 4f8ada444cc7a7ea70cdc81f098b34c5f1f2df41 Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Sun, 14 Feb 2010 18:15:51 +0100 Subject: ALSA: Echoaudio - Add firmware cache #2 This patch implements a simple cache for the firmware files when CONFIG_PM is defined. This patch changes get_firmware(), free_firmware() and adds free_firmware_cache(). The first two functions implement a very simple cache and the latter is used to actually release all the stored firmwares when the module is unloaded. When CONFIG_PM is not enabled those functions act as before, that is free_firmware() releases the firmware immediately and free_firmware_cache() does nothing. Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 42 +++++++++++++++++++++++++++++++++++++---- sound/pci/echoaudio/echoaudio.h | 3 +++ 2 files changed, 41 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 78fc2637bfa6..79dde9592847 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -43,12 +43,24 @@ static int get_firmware(const struct firmware **fw_entry, { int err; char name[30]; - const struct firmware *frm = &card_fw[fw_index]; - DE_ACT(("firmware requested: %s\n", frm->data)); - snprintf(name, sizeof(name), "ea/%s", frm->data); - if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0) +#ifdef CONFIG_PM + if (chip->fw_cache[fw_index]) { + DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data)); + *fw_entry = chip->fw_cache[fw_index]; + return 0; + } +#endif + + DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data)); + snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data); + err = request_firmware(fw_entry, name, pci_device(chip)); + if (err < 0) snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err); +#ifdef CONFIG_PM + else + chip->fw_cache[fw_index] = *fw_entry; +#endif return err; } @@ -56,8 +68,29 @@ static int get_firmware(const struct firmware **fw_entry, static void free_firmware(const struct firmware *fw_entry) { +#ifdef CONFIG_PM + DE_ACT(("firmware not released (kept in cache)\n")); +#else release_firmware(fw_entry); DE_ACT(("firmware released\n")); +#endif +} + + + +static void free_firmware_cache(struct echoaudio *chip) +{ +#ifdef CONFIG_PM + int i; + + for (i = 0; i < 8 ; i++) + if (chip->fw_cache[i]) { + release_firmware(chip->fw_cache[i]); + DE_ACT(("release_firmware(%d)\n", i)); + } + + DE_ACT(("firmware_cache released\n")); +#endif } @@ -1880,6 +1913,7 @@ static int snd_echo_free(struct echoaudio *chip) pci_disable_device(chip->pci); /* release chip data */ + free_firmware_cache(chip); kfree(chip); DE_INIT(("Chip freed.\n")); return 0; diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index be76ef3b829a..a84c0d15cc50 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -449,6 +449,9 @@ struct echoaudio { volatile u32 __iomem *dsp_registers; /* DSP's register base */ u32 active_mask; /* Chs. active mask or * punks out */ +#ifdef CONFIG_PM + const struct firmware *fw_cache[8]; /* Cached firmwares */ +#endif #ifdef ECHOCARD_HAS_MIDI u16 mtc_state; /* State for MIDI input parsing state machine */ -- cgit v1.2.2 From ad3499f4668f684ef6e5d0222ae14d5e4ade1fdd Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Sun, 14 Feb 2010 18:15:59 +0100 Subject: ALSA: Echoaudio - Add suspend support #1 Move the controls init code outside the init_hw() function because is must not be called during resume. This patch moves the code that initializes the card's controls with default valued from the init_hw() function into a separated set_mixer_defaults() function (one for each of the 16 supported cards). This change is necessary because during resume we must resurrect the hardware without losing the previous settings. set_mixer_defaults() must be called only once when the module is loaded. Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/darla20_dsp.c | 10 +++++++--- sound/pci/echoaudio/darla24_dsp.c | 10 +++++++--- sound/pci/echoaudio/echo3g_dsp.c | 26 ++++++++++++-------------- sound/pci/echoaudio/gina20_dsp.c | 13 ++++++++----- sound/pci/echoaudio/gina24_dsp.c | 20 ++++++++++---------- sound/pci/echoaudio/indigo_dsp.c | 10 +++++++--- sound/pci/echoaudio/indigo_express_dsp.c | 1 + sound/pci/echoaudio/indigodj_dsp.c | 10 +++++++--- sound/pci/echoaudio/indigodjx_dsp.c | 11 +++++++---- sound/pci/echoaudio/indigoio_dsp.c | 10 +++++++--- sound/pci/echoaudio/indigoiox_dsp.c | 11 +++++++---- sound/pci/echoaudio/layla20_dsp.c | 13 ++++++++----- sound/pci/echoaudio/layla24_dsp.c | 18 ++++++++++-------- sound/pci/echoaudio/mia_dsp.c | 10 +++++++--- sound/pci/echoaudio/mona_dsp.c | 22 ++++++++++------------ 15 files changed, 115 insertions(+), 80 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index a44135d6acbb..20c7cbc89bb3 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -57,15 +57,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + /* The Darla20 has no external clock sources */ static u32 detect_input_clocks(const struct echoaudio *chip) { diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index d681da180829..6da6663e9176 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -56,15 +56,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index f0071935c0cb..3cdc2ee2d1dd 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -97,20 +97,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; - chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->non_audio_spdif = FALSE; - chip->bad_board = FALSE; - - if ((err = init_line_levels(chip)) < 0) - return err; - err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); - if (err < 0) - return err; - err = set_phantom_power(chip, 0); - if (err < 0) - return err; - err = set_professional_spdif(chip, TRUE); DE_INIT(("init_hw done\n")); return err; @@ -118,6 +104,18 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->non_audio_spdif = FALSE; + chip->bad_board = FALSE; + chip->phantom_power = FALSE; + return init_line_levels(chip); +} + + + static int set_phantom_power(struct echoaudio *chip, char on) { u32 control_reg = le32_to_cpu(chip->comm_page->control_register); diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index c5de88b6792d..d1615a0579d1 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -62,17 +62,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - - err = set_professional_spdif(chip, TRUE); - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->professional_spdif = FALSE; + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 093dd7ba0e81..98f7cfa81b5f 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -57,9 +57,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 | ECHO_CLOCK_BIT_ADAT; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; - chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; /* Gina24 comes in both '301 and '361 flavors */ if (chip->device_id == DEVICE_ID_56361) { @@ -81,19 +78,22 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); - if (err < 0) - return err; - err = set_professional_spdif(chip, TRUE); - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index 8799d2e6536a..5e85f14fe5a8 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { return ECHO_CLOCK_BIT_INTERNAL; diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c index 9ab625e15652..2e4ab3e34a74 100644 --- a/sound/pci/echoaudio/indigo_express_dsp.c +++ b/sound/pci/echoaudio/indigo_express_dsp.c @@ -61,6 +61,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg |= clock; if (control_reg != old_control_reg) { + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); chip->comm_page->control_register = cpu_to_le32(control_reg); chip->sample_rate = rate; clear_handshake(chip); diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index cb1c92ca9fef..68f3c8ccc1bf 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { return ECHO_CLOCK_BIT_INTERNAL; diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index 91dbfeb586a7..bb9632c752a9 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -59,10 +59,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - err = init_line_levels(chip); - if (err < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } + + + +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index 134e783d3486..beb9a5b69892 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { return ECHO_CLOCK_BIT_INTERNAL; diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index 766cf501799d..394c6e76bcbc 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -59,10 +59,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - err = init_line_levels(chip); - if (err < 0) - return err; - DE_INIT(("init_hw done\n")); return err; } + + + +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 07f32454757e..53ce94605044 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -64,17 +64,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - - err = set_professional_spdif(chip, TRUE); - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->professional_spdif = FALSE; + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index 12dc00adca9f..8c041647f285 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -61,9 +61,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; - chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; if ((err = load_firmware(chip)) < 0) return err; @@ -72,17 +69,22 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = init_line_levels(chip)) < 0) return err; - err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); - if (err < 0) - return err; - err = set_professional_spdif(chip, TRUE); - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index d0302f2f00db..6ebfa6e7ab9e 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -66,15 +66,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip))) - return err; - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index b28b8e4703cf..6e6a7eb555b8 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -67,28 +67,26 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) else chip->dsp_code_to_load = FW_MONA_301_DSP; - chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; - chip->professional_spdif = FALSE; - chip->digital_in_automute = TRUE; - if ((err = load_firmware(chip)) < 0) return err; chip->bad_board = FALSE; - if ((err = init_line_levels(chip)) < 0) - return err; - - err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); - if (err < 0) - return err; - err = set_professional_spdif(chip, TRUE); - DE_INIT(("init_hw done\n")); return err; } +static int set_mixer_defaults(struct echoaudio *chip) +{ + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + return init_line_levels(chip); +} + + + static u32 detect_input_clocks(const struct echoaudio *chip) { u32 clocks_from_dsp, clock_bits; -- cgit v1.2.2 From 47b5d028fdce8f809bf22852ac900338fb90e8aa Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Sun, 14 Feb 2010 18:16:10 +0100 Subject: ALSA: Echoaudio - Add suspend support #2 This patch adds rearranges parts of the initialization code and adds suspend and resume callbacks. This patch adds suspend and resume callbacks. It also rearranges parts of the initialization code so it can be used in both the first initialization (when the module is loaded we also have to load default settings) and the resume callback (where we have to restore the previous settings). Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 153 ++++++++++++++++++++++++++++++++---- sound/pci/echoaudio/echoaudio.h | 2 + sound/pci/echoaudio/echoaudio_dsp.c | 145 +++++++++++++++++++--------------- 3 files changed, 222 insertions(+), 78 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 79dde9592847..2783ce6c236e 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -753,6 +753,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->lock); switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + DE_ACT(("pcm_trigger resume\n")); case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: DE_ACT(("pcm_trigger start\n")); @@ -776,6 +778,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) err = start_transport(chip, channelmask, chip->pipe_cyclic_mask); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + DE_ACT(("pcm_trigger suspend\n")); case SNDRV_PCM_TRIGGER_STOP: DE_ACT(("pcm_trigger stop\n")); for (i = 0; i < DSP_MAXPIPES; i++) { @@ -1951,18 +1955,27 @@ static __devinit int snd_echo_create(struct snd_card *card, return err; pci_set_master(pci); - /* allocate a chip-specific data */ - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) { - pci_disable_device(pci); - return -ENOMEM; + /* Allocate chip if needed */ + if (!*rchip) { + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + pci_disable_device(pci); + return -ENOMEM; + } + DE_INIT(("chip=%p\n", chip)); + spin_lock_init(&chip->lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + atomic_set(&chip->opencount, 0); + mutex_init(&chip->mode_mutex); + chip->can_set_rate = 1; + } else { + /* If this was called from the resume function, chip is + * already allocated and it contains current card settings. + */ + chip = *rchip; } - DE_INIT(("chip=%p\n", chip)); - - spin_lock_init(&chip->lock); - chip->card = card; - chip->pci = pci; - chip->irq = -1; /* PCI resource allocation */ chip->dsp_registers_phys = pci_resource_start(pci, 0); @@ -2002,7 +2015,9 @@ static __devinit int snd_echo_create(struct snd_card *card, chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area; err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); - if (err) { + if (err >= 0) + err = set_mixer_defaults(chip); + if (err < 0) { DE_INIT(("init_hw err=%d\n", err)); snd_echo_free(chip); return err; @@ -2013,9 +2028,6 @@ static __devinit int snd_echo_create(struct snd_card *card, snd_echo_free(chip); return err; } - atomic_set(&chip->opencount, 0); - mutex_init(&chip->mode_mutex); - chip->can_set_rate = 1; *rchip = chip; /* Init done ! */ return 0; @@ -2048,6 +2060,7 @@ static int __devinit snd_echo_probe(struct pci_dev *pci, snd_card_set_dev(card, &pci->dev); + chip = NULL; /* Tells snd_echo_create to allocate chip */ if ((err = snd_echo_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; @@ -2187,6 +2200,112 @@ ctl_error: +#if defined(CONFIG_PM) + +static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct echoaudio *chip = pci_get_drvdata(pci); + + DE_INIT(("suspend start\n")); + snd_pcm_suspend_all(chip->analog_pcm); + snd_pcm_suspend_all(chip->digital_pcm); + +#ifdef ECHOCARD_HAS_MIDI + /* This call can sleep */ + if (chip->midi_out) + snd_echo_midi_output_trigger(chip->midi_out, 0); +#endif + spin_lock_irq(&chip->lock); + if (wait_handshake(chip)) { + spin_unlock_irq(&chip->lock); + return -EIO; + } + clear_handshake(chip); + if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) { + spin_unlock_irq(&chip->lock); + return -EIO; + } + spin_unlock_irq(&chip->lock); + + chip->dsp_code = NULL; + free_irq(chip->irq, chip); + chip->irq = -1; + pci_save_state(pci); + pci_disable_device(pci); + + DE_INIT(("suspend done\n")); + return 0; +} + + + +static int snd_echo_resume(struct pci_dev *pci) +{ + struct echoaudio *chip = pci_get_drvdata(pci); + struct comm_page *commpage, *commpage_bak; + u32 pipe_alloc_mask; + int err; + + DE_INIT(("resume start\n")); + pci_restore_state(pci); + commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL); + commpage = chip->comm_page; + memcpy(commpage_bak, commpage, sizeof(struct comm_page)); + + err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); + if (err < 0) { + kfree(commpage_bak); + DE_INIT(("resume init_hw err=%d\n", err)); + snd_echo_free(chip); + return err; + } + DE_INIT(("resume init OK\n")); + + /* Temporarily set chip->pipe_alloc_mask=0 otherwise + * restore_dsp_settings() fails. + */ + pipe_alloc_mask = chip->pipe_alloc_mask; + chip->pipe_alloc_mask = 0; + err = restore_dsp_rettings(chip); + chip->pipe_alloc_mask = pipe_alloc_mask; + if (err < 0) { + kfree(commpage_bak); + return err; + } + DE_INIT(("resume restore OK\n")); + + memcpy(&commpage->audio_format, &commpage_bak->audio_format, + sizeof(commpage->audio_format)); + memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr, + sizeof(commpage->sglist_addr)); + memcpy(&commpage->midi_output, &commpage_bak->midi_output, + sizeof(commpage->midi_output)); + kfree(commpage_bak); + + if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED, + ECHOCARD_NAME, chip)) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + DE_INIT(("resume irq=%d\n", chip->irq)); + +#ifdef ECHOCARD_HAS_MIDI + if (chip->midi_input_enabled) + enable_midi_input(chip, TRUE); + if (chip->midi_out) + snd_echo_midi_output_trigger(chip->midi_out, 1); +#endif + + DE_INIT(("resume done\n")); + return 0; +} + +#endif /* CONFIG_PM */ + + + static void __devexit snd_echo_remove(struct pci_dev *pci) { struct echoaudio *chip; @@ -2209,6 +2328,10 @@ static struct pci_driver driver = { .id_table = snd_echo_ids, .probe = snd_echo_probe, .remove = __devexit_p(snd_echo_remove), +#ifdef CONFIG_PM + .suspend = snd_echo_suspend, + .resume = snd_echo_resume, +#endif /* CONFIG_PM */ }; diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index a84c0d15cc50..1df974dcb5f4 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -472,6 +472,8 @@ static void free_firmware(const struct firmware *fw_entry); #ifdef ECHOCARD_HAS_MIDI static int enable_midi_input(struct echoaudio *chip, char enable); +static void snd_echo_midi_output_trigger( + struct snd_rawmidi_substream *substream, int up); static int midi_service_irq(struct echoaudio *chip); static int __devinit snd_echo_midi_create(struct snd_card *card, struct echoaudio *chip); diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 031ef7e9da91..64417a733220 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -497,9 +497,6 @@ static int load_firmware(struct echoaudio *chip) if ((box_type = load_asic(chip)) < 0) return box_type; /* error */ - if ((err = restore_dsp_rettings(chip)) < 0) - return err; - return box_type; } @@ -659,51 +656,106 @@ static void get_audio_meters(struct echoaudio *chip, long *meters) static int restore_dsp_rettings(struct echoaudio *chip) { - int err; + int i, o, err; DE_INIT(("restore_dsp_settings\n")); if ((err = check_asic_status(chip)) < 0) return err; - /* @ Gina20/Darla20 only. Should be harmless for other cards. */ + /* Gina20/Darla20 only. Should be harmless for other cards. */ chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF; chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF; chip->comm_page->handshake = 0xffffffff; - if ((err = set_sample_rate(chip, chip->sample_rate)) < 0) + /* Restore output busses */ + for (i = 0; i < num_busses_out(chip); i++) { + err = set_output_gain(chip, i, chip->output_gain[i]); + if (err < 0) + return err; + } + +#ifdef ECHOCARD_HAS_VMIXER + for (i = 0; i < num_pipes_out(chip); i++) + for (o = 0; o < num_busses_out(chip); o++) { + err = set_vmixer_gain(chip, o, i, + chip->vmixer_gain[o][i]); + if (err < 0) + return err; + } + if (update_vmixer_level(chip) < 0) + return -EIO; +#endif /* ECHOCARD_HAS_VMIXER */ + +#ifdef ECHOCARD_HAS_MONITOR + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) { + err = set_monitor_gain(chip, o, i, + chip->monitor_gain[o][i]); + if (err < 0) + return err; + } +#endif /* ECHOCARD_HAS_MONITOR */ + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) { + err = set_input_gain(chip, i, chip->input_gain[i]); + if (err < 0) + return err; + } +#endif /* ECHOCARD_HAS_INPUT_GAIN */ + + err = update_output_line_level(chip); + if (err < 0) return err; - if (chip->meters_enabled) - if (send_vector(chip, DSP_VC_METERS_ON) < 0) - return -EIO; + err = update_input_line_level(chip); + if (err < 0) + return err; -#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK - if (set_input_clock(chip, chip->input_clock) < 0) + err = set_sample_rate(chip, chip->sample_rate); + if (err < 0) + return err; + + if (chip->meters_enabled) { + err = send_vector(chip, DSP_VC_METERS_ON); + if (err < 0) + return err; + } + +#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH + if (set_digital_mode(chip, chip->digital_mode) < 0) return -EIO; #endif -#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH - if (set_output_clock(chip, chip->output_clock) < 0) +#ifdef ECHOCARD_HAS_DIGITAL_IO + if (set_professional_spdif(chip, chip->professional_spdif) < 0) return -EIO; #endif - if (update_output_line_level(chip) < 0) +#ifdef ECHOCARD_HAS_PHANTOM_POWER + if (set_phantom_power(chip, chip->phantom_power) < 0) return -EIO; +#endif - if (update_input_line_level(chip) < 0) +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + /* set_input_clock() also restores automute setting */ + if (set_input_clock(chip, chip->input_clock) < 0) return -EIO; +#endif -#ifdef ECHOCARD_HAS_VMIXER - if (update_vmixer_level(chip) < 0) +#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH + if (set_output_clock(chip, chip->output_clock) < 0) return -EIO; #endif if (wait_handshake(chip) < 0) return -EIO; clear_handshake(chip); + if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0) + return -EIO; DE_INIT(("restore_dsp_rettings done\n")); - return send_vector(chip, DSP_VC_UPDATE_FLAGS); + return 0; } @@ -920,9 +972,6 @@ static int init_dsp_comm_page(struct echoaudio *chip) chip->card_name = ECHOCARD_NAME; chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ - chip->digital_mode = DIGITAL_MODE_NONE; - chip->input_clock = ECHO_CLOCK_INTERNAL; - chip->output_clock = ECHO_CLOCK_WORD; chip->asic_loaded = FALSE; memset(chip->comm_page, 0, sizeof(struct comm_page)); @@ -933,7 +982,6 @@ static int init_dsp_comm_page(struct echoaudio *chip) chip->comm_page->midi_out_free_count = cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE); chip->comm_page->sample_rate = cpu_to_le32(44100); - chip->sample_rate = 44100; /* Set line levels so we don't blast any inputs on startup */ memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE); @@ -944,50 +992,21 @@ static int init_dsp_comm_page(struct echoaudio *chip) -/* This function initializes the several volume controls for busses and pipes. -This MUST be called after the DSP is up and running ! */ +/* This function initializes the chip structure with default values, ie. all + * muted and internal clock source. Then it copies the settings to the DSP. + * This MUST be called after the DSP is up and running ! + */ static int init_line_levels(struct echoaudio *chip) { - int st, i, o; - DE_INIT(("init_line_levels\n")); - - /* Mute output busses */ - for (i = 0; i < num_busses_out(chip); i++) - if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED))) - return st; - if ((st = update_output_line_level(chip))) - return st; - -#ifdef ECHOCARD_HAS_VMIXER - /* Mute the Vmixer */ - for (i = 0; i < num_pipes_out(chip); i++) - for (o = 0; o < num_busses_out(chip); o++) - if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED))) - return st; - if ((st = update_vmixer_level(chip))) - return st; -#endif /* ECHOCARD_HAS_VMIXER */ - -#ifdef ECHOCARD_HAS_MONITOR - /* Mute the monitor mixer */ - for (o = 0; o < num_busses_out(chip); o++) - for (i = 0; i < num_busses_in(chip); i++) - if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED))) - return st; - if ((st = update_output_line_level(chip))) - return st; -#endif /* ECHOCARD_HAS_MONITOR */ - -#ifdef ECHOCARD_HAS_INPUT_GAIN - for (i = 0; i < num_busses_in(chip); i++) - if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED))) - return st; - if ((st = update_input_line_level(chip))) - return st; -#endif /* ECHOCARD_HAS_INPUT_GAIN */ - - return 0; + memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain)); + memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain)); + memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain)); + memset(chip->vmixer_gain, ECHOGAIN_MUTED, sizeof(chip->vmixer_gain)); + chip->input_clock = ECHO_CLOCK_INTERNAL; + chip->output_clock = ECHO_CLOCK_WORD; + chip->sample_rate = 44100; + return restore_dsp_rettings(chip); } -- cgit v1.2.2 From 0a27fcfaaf61108d94f0377f91bed81b2dd35f52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 15 Feb 2010 17:05:28 +0100 Subject: ALSA: hda - Correct ASUA blacklist for MSI brokenness The MSI blacklist entry for ASUS mobo added in the commit 8ce28d6abff34886d3797b25324c940471b99164 was based on the alsa-info output wrongly posted. Fix the id to the right one now. Reported-by: Sid Boyce Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3600e9cc9bc6..ff6da6f386d1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2350,7 +2350,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) */ static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ - SND_PCI_QUIRK(0x1043, 0x829c, "ASUS", 0), /* nvidia */ + SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ {} }; -- cgit v1.2.2 From b721e68bdc5b39c51bf6a1469f8d3663fbe03243 Mon Sep 17 00:00:00 2001 From: Giuliano Pochini Date: Wed, 17 Feb 2010 00:57:44 +0100 Subject: ALSA: Echoaudio, fix Guru Meditation #00000005.48454C50 This patch fixes a division by zero error in the irq handler. There is a small window between the hw_params() callback and when runtime->frame_bits is set by ALSA middle layer. When another substream is already running, if an interrupt is delivered during that window the irq handler calls pcm_pointer() which does a division by zero. The patch below makes the irq handler skip substreams that are initialized but not started yet. Cc to Clemens Ladisch because he proposed an alternate fix. For more information, please read the original thread in the linux-kernel mailing list: http://lkml.org/lkml/2010/2/2/187 Signed-off-by: Giuliano Pochini Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 1305f7ca02c3..641d7f07392c 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1821,7 +1821,9 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) /* The hardware doesn't tell us which substream caused the irq, thus we have to check all running substreams. */ for (ss = 0; ss < DSP_MAXPIPES; ss++) { - if ((substream = chip->substream[ss])) { + substream = chip->substream[ss]; + if (substream && ((struct audiopipe *)substream->runtime-> + private_data)->state == PIPE_STATE_STARTED) { period = pcm_pointer(substream) / substream->runtime->period_size; if (period != chip->last_period[ss]) { -- cgit v1.2.2 From 7fb2d723e65cc793213515fa1da092b7c92a5b48 Mon Sep 17 00:00:00 2001 From: Florian Zumbiehl Date: Thu, 18 Feb 2010 07:01:20 +0100 Subject: ALSA: cs46xx - Do test writes to register AC97_REC_GAIN in snd_cs46xx_codec_reset() bypassing the register cache, so as to not clobber the cached register value during resume. Signed-off-by: Florian Zumbiehl Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 56fcf00c0e27..9fea5bb448cd 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2266,7 +2266,7 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97) return; /* test if we can write to the record gain volume register */ - snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); + snd_ac97_write(ac97, AC97_REC_GAIN, 0x8a05); if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) return; -- cgit v1.2.2 From 04510a74bfbcbfd53dd48b3094aad89d5eca1d28 Mon Sep 17 00:00:00 2001 From: Florian Zumbiehl Date: Thu, 18 Feb 2010 07:03:55 +0100 Subject: ALSA: cs46xx - fix some typos Signed-off-by: Florian Zumbiehl Signed-off-by: Takashi Iwai --- sound/pci/cs46xx/cs46xx_lib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 9fea5bb448cd..3f99a5e8528c 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2238,11 +2238,11 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97) /* set the desired CODEC mode */ if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) { - snd_printdd("cs46xx: CODOEC1 mode %04x\n",0x0); - snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x0); + snd_printdd("cs46xx: CODEC1 mode %04x\n", 0x0); + snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x0); } else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) { - snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3); - snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3); + snd_printdd("cs46xx: CODEC2 mode %04x\n", 0x3); + snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x3); } else { snd_BUG(); /* should never happen ... */ } -- cgit v1.2.2 From ba579eb7b30791751f556ee01905636cda50c864 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sat, 20 Feb 2010 11:16:30 -0500 Subject: ALSA: hda: Use 3stack quirk for Toshiba Satellite L40-10Q BugLink: https://bugs.launchpad.net/bugs/524948 The OR has verified that the existing model=laptop-eapd quirk does not function correctly but instead needs model=3stack. Make this change so that manual corrections to module-init-tools file(s) are not required. Reported-by: Lasse Havelund CC: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 21011b5199de..7832f363496f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1098,7 +1098,7 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), - SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), -- cgit v1.2.2 From e458b1fadf9239d1fdb165ff4c4ea0d807041bec Mon Sep 17 00:00:00 2001 From: Luke Yelavich Date: Fri, 12 Feb 2010 16:28:29 +1100 Subject: ALSA: hda - Add Macmini 3,1 support BugLink: https://bugs.edge.launchpad.net/ubuntu/+source/linux/+bug/343989 Add a model quirk for the NVIDIA based Macmini hardware, aka Macmini 3,1. The pinout is almost identical to the mb5 quirk, except for no microphone and the line-in mixer controls being on a different index. Everything works in 2ch mode, but as I am not sure what needs to be changed for 6ch mode, or whether the Mac Mini's chip supports 6ch mode, I have simply duplicated the code from the mb5 quirk for the mac mini chmode management. The new model parameter for this quirk is "macmini3". Signed-off-by: Luke Yelavich Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 136 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0c224977c8cf..b5a6ba025930 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -211,6 +211,7 @@ enum { ALC885_MACPRO, ALC885_MBP3, ALC885_MB5, + ALC885_MACMINI3, ALC885_IMAC24, ALC885_IMAC91, ALC883_3ST_2ch_DIG, @@ -6751,6 +6752,14 @@ static struct hda_input_mux mb5_capture_source = { }, }; +static struct hda_input_mux macmini3_capture_source = { + .num_items = 2, + .items = { + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; + static struct hda_input_mux alc883_3stack_6ch_intel = { .num_items = 4, .items = { @@ -6999,6 +7008,35 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = { { 6, alc885_mb5_ch6_init }, }; +/* + * 2ch + * Speakers/Woofer/HP = Front + * LineIn = Input + */ +static struct hda_verb alc885_macmini3_ch2_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } /* end */ +}; + +/* + * 6ch mode + * Speakers/HP = Front + * Woofer = LFE + * LineIn = Surround + */ +static struct hda_verb alc885_macmini3_ch6_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + { } /* end */ +}; + +static struct hda_channel_mode alc885_macmini3_6ch_modes[2] = { + { 2, alc885_mb5_ch2_init }, + { 6, alc885_mb5_ch6_init }, +}; + /* * 2ch mode @@ -7243,6 +7281,21 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc885_macmini3_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc885_imac91_mixer[] = { HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT), HDA_BIND_MUTE ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT), @@ -7617,6 +7670,53 @@ static struct hda_verb alc885_mb5_init_verbs[] = { { } }; +/* Macmini 3,1 */ +static struct hda_verb alc885_macmini3_init_verbs[] = { + /* DACs */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Surround mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* LFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* HP mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* LFE Pin (0x0e) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* HP Pin (0x0f) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x03}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* Line In pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + { } +}; + /* Macbook Pro rev3 */ static struct hda_verb alc885_mbp3_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ @@ -7800,6 +7900,18 @@ static void alc885_mb5_automute(struct hda_codec *codec) } +static void alc885_macmini3_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + static void alc885_mb5_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -7808,6 +7920,14 @@ static void alc885_mb5_unsol_event(struct hda_codec *codec, alc885_mb5_automute(codec); } +static void alc885_macmini3_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Headphone insertion or removal. */ + if ((res >> 26) == ALC880_HP_EVENT) + alc885_mb5_automute(codec); +} + static void alc885_imac91_automute(struct hda_codec *codec) { unsigned int present; @@ -8974,6 +9094,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC882_ASUS_A7M] = "asus-a7m", [ALC885_MACPRO] = "macpro", [ALC885_MB5] = "mb5", + [ALC885_MACMINI3] = "macmini3", [ALC885_MBP3] = "mbp3", [ALC885_IMAC24] = "imac24", [ALC885_IMAC91] = "imac91", @@ -9157,6 +9278,7 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = { */ SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5), SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3), {} /* terminator */ }; @@ -9238,6 +9360,20 @@ static struct alc_config_preset alc882_presets[] = { .unsol_event = alc885_mb5_unsol_event, .init_hook = alc885_mb5_automute, }, + [ALC885_MACMINI3] = { + .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_macmini3_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_macmini3_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes), + .input_mux = &macmini3_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc885_macmini3_unsol_event, + .init_hook = alc885_macmini3_automute, + }, [ALC885_MACPRO] = { .mixers = { alc882_macpro_mixer }, .init_verbs = { alc882_macpro_init_verbs }, -- cgit v1.2.2 From 9d54f08bc77bf6dfe835b945d03b6e127c9fc5a3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Feb 2010 08:34:40 +0100 Subject: ALSA: hda - Clean up Intel Mac unsol codes Use the standard unsol_event callback with each setup callback for IntelMac models with Realtek ALC885 codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 81 +++++++++---------------------------------- 1 file changed, 17 insertions(+), 64 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b5a6ba025930..f8fb260a2dd7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7879,6 +7879,9 @@ static void alc885_imac24_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[1] = 0x1a; } +#define alc885_mb5_setup alc885_imac24_setup +#define alc885_macmini3_setup alc885_imac24_setup + static void alc885_mbp3_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -7887,66 +7890,13 @@ static void alc885_mbp3_setup(struct hda_codec *codec) spec->autocfg.speaker_pins[0] = 0x14; } -static void alc885_mb5_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - -} - -static void alc885_macmini3_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -static void alc885_mb5_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Headphone insertion or removal. */ - if ((res >> 26) == ALC880_HP_EVENT) - alc885_mb5_automute(codec); -} - -static void alc885_macmini3_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Headphone insertion or removal. */ - if ((res >> 26) == ALC880_HP_EVENT) - alc885_mb5_automute(codec); -} - -static void alc885_imac91_automute(struct hda_codec *codec) +static void alc885_imac91_setup(struct hda_codec *codec) { - unsigned int present; - - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - -} + struct alc_spec *spec = codec->spec; -static void alc885_imac91_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - /* Headphone insertion or removal. */ - if ((res >> 26) == ALC880_HP_EVENT) - alc885_imac91_automute(codec); + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x15; + spec->autocfg.speaker_pins[1] = 0x1a; } static struct hda_verb alc882_targa_verbs[] = { @@ -9357,8 +9307,9 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &mb5_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc885_mb5_unsol_event, - .init_hook = alc885_mb5_automute, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_mb5_setup, + .init_hook = alc_automute_amp, }, [ALC885_MACMINI3] = { .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer }, @@ -9371,8 +9322,9 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &macmini3_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc885_macmini3_unsol_event, - .init_hook = alc885_macmini3_automute, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_macmini3_setup, + .init_hook = alc_automute_amp, }, [ALC885_MACPRO] = { .mixers = { alc882_macpro_mixer }, @@ -9411,8 +9363,9 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &alc882_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, .dig_in_nid = ALC882_DIGIN_NID, - .unsol_event = alc885_imac91_unsol_event, - .init_hook = alc885_imac91_automute, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_imac91_setup, + .init_hook = alc_automute_amp, }, [ALC882_TARGA] = { .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, -- cgit v1.2.2 From 2448158ed2ae64ef3219b51e0176a4e1151ba9ec Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Mon, 8 Feb 2010 20:37:26 +0100 Subject: ALSA: Typo. s/distrubs/disturbs/ Signed-off-by: Paul Menzel Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 06f230f518b7..051cf5145330 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1411,7 +1411,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) chip->codec_mask &= ~(1 << c); /* More badly, accessing to a non-existing * codec often screws up the controller chip, - * and distrubs the further communications. + * and disturbs the further communications. * Thus if an error occurs during probing, * better to reset the controller chip to * get back to the sanity state. -- cgit v1.2.2 From 0708cc582f0fe2578eaab722841caf2b4f8cfe37 Mon Sep 17 00:00:00 2001 From: Paul Menzel Date: Mon, 8 Feb 2010 20:42:46 +0100 Subject: ALSA: hda-intel: Add position_fix quirk for ASUS M2V-MX SE. With PulseAudio and an application accessing an input device like `gnome-volume-manager` both have high CPU load as reported in [1]. Loading `snd-hda-intel` with `position_fix=1` fixes this issue. Therefore add a quirk for ASUS M2V-MX SE. The only downside is, when now exiting for example MPlayer when it is playing an audio file a high pitched sound is outputted by the speaker. $ lspci -vvnn | grep -A10 Audio 20:01.0 Audio device [0403]: VIA Technologies, Inc. VT1708/A [Azalia HDAC] (VIA High Definition Audio Controller) [1106:3288] (rev 10) Subsystem: ASUSTeK Computer Inc. Device [1043:8290] Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx- Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- SERR- Kernel driver in use: HDA Intel [1] http://sourceforge.net/mailarchive/forum.php?thread_name=1265550675.4642.24.camel%40mattotaupa&forum_name=alsa-user Signed-off-by: Paul Menzel Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 051cf5145330..22dcdc201ede 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2264,6 +2264,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), {} -- cgit v1.2.2 From bf30a4309d4294d3eca248ea8a20c1c3570f5e74 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 22 Feb 2010 10:33:13 +0100 Subject: ALSA: via82xx: add quirk for D1289 motherboard Add a headphones-only quirk for the Fujitsu Siemens D1289. Signed-off-by: Clemens Ladisch Reported-and-tested-by: Marc Haber Cc: Signed-off-by: Jaroslav Kysela --- sound/pci/via82xx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8a332d2f615c..03d6aea19749 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1790,6 +1790,12 @@ static struct ac97_quirk ac97_quirks[] = { .name = "ASRock K7VT2", .type = AC97_TUNE_HP_ONLY }, + { + .subvendor = 0x110a, + .subdevice = 0x0079, + .name = "Fujitsu Siemens D1289", + .type = AC97_TUNE_HP_ONLY + }, { .subvendor = 0x1019, .subdevice = 0x0a81, -- cgit v1.2.2 From d01aecdf900574cf6be7c1c6114e708801126baf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 23 Feb 2010 08:07:15 +0100 Subject: ALSA: hda - Remove identical definitions for macmini3 model The channel mode definitions for macmini3 model are identical with mb5. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f8fb260a2dd7..c74ca39a0b8e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7008,35 +7008,7 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = { { 6, alc885_mb5_ch6_init }, }; -/* - * 2ch - * Speakers/Woofer/HP = Front - * LineIn = Input - */ -static struct hda_verb alc885_macmini3_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - { } /* end */ -}; - -/* - * 6ch mode - * Speakers/HP = Front - * Woofer = LFE - * LineIn = Surround - */ -static struct hda_verb alc885_macmini3_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, - { } /* end */ -}; - -static struct hda_channel_mode alc885_macmini3_6ch_modes[2] = { - { 2, alc885_mb5_ch2_init }, - { 6, alc885_mb5_ch6_init }, -}; - +#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes /* * 2ch mode -- cgit v1.2.2 From 32679f95cac3b1bdf27dce8b5273e06af186fd91 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 22 Feb 2010 17:31:09 -0800 Subject: ALSA: hda - enable snoop for Intel Cougar Point This patch enables snoop, eliminating static during playback. This patch supersedes the previous Cougar Point audio patch. Signed-off-by: Seth Heasley Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 22dcdc201ede..1adac8cc9592 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -448,6 +448,7 @@ struct azx { /* driver types */ enum { AZX_DRIVER_ICH, + AZX_DRIVER_PCH, AZX_DRIVER_SCH, AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, @@ -462,6 +463,7 @@ enum { static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", + [AZX_DRIVER_PCH] = "HDA Intel PCH", [AZX_DRIVER_SCH] = "HDA Intel MID", [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", @@ -1064,6 +1066,7 @@ static void azx_init_pci(struct azx *chip) 0x01, NVIDIA_HDA_ENABLE_COHBIT); break; case AZX_DRIVER_SCH: + case AZX_DRIVER_PCH: pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) { pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, @@ -2421,6 +2424,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, if (bdl_pos_adj[dev] < 0) { switch (chip->driver_type) { case AZX_DRIVER_ICH: + case AZX_DRIVER_PCH: bdl_pos_adj[dev] = 1; break; default: @@ -2700,7 +2704,7 @@ static struct pci_device_id azx_ids[] = { /* PCH */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH }, /* CPT */ - { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH }, /* SCH */ { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH }, /* ATI SB 450/600 */ -- cgit v1.2.2 From 76e6f5a9efc919f9179163c66403451a789d47ab Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Tue, 23 Feb 2010 01:19:51 -0800 Subject: ALSA: add support for Macbook Air 2,1 internal speaker Add support for Macbook Air 2,1 (late 2008) internal speaker and headphones. Create a "mba21" model for snd-hda-intel. Signed-off-by: Reimundo Heluani Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c74ca39a0b8e..5382872eba1f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -209,6 +209,7 @@ enum { ALC882_ASUS_A7J, ALC882_ASUS_A7M, ALC885_MACPRO, + ALC885_MBA21, ALC885_MBP3, ALC885_MB5, ALC885_MACMINI3, @@ -6948,6 +6949,13 @@ static struct hda_channel_mode alc882_sixstack_modes[2] = { { 8, alc882_sixstack_ch8_init }, }; + +/* Macbook Air 2,1 */ + +static struct hda_channel_mode alc885_mba21_ch_modes[1] = { + { 2, NULL }, +}; + /* * macbook pro ALC885 can switch LineIn to LineOut without losing Mic */ @@ -7220,6 +7228,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = { { } /* end */ }; +/* Macbook Air 2,1 same control for HP and internal Speaker */ + +static struct snd_kcontrol_new alc885_mba21_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT), + { } +}; + + static struct snd_kcontrol_new alc885_mbp3_mixer[] = { HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT), HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT), @@ -7689,6 +7706,29 @@ static struct hda_verb alc885_macmini3_init_verbs[] = { { } }; + +static struct hda_verb alc885_mba21_init_verbs[] = { + /*Internal and HP Speaker Mixer*/ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /*Internal Speaker Pin (0x0c)*/ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP Pin: output 0 (0x0e) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)}, + /* Line in (is hp when jack connected)*/ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + { } + }; + + /* Macbook Pro rev3 */ static struct hda_verb alc885_mbp3_init_verbs[] = { /* Front mixer: unmute input/output amp left and right (volume = 0) */ @@ -7854,6 +7894,17 @@ static void alc885_imac24_setup(struct hda_codec *codec) #define alc885_mb5_setup alc885_imac24_setup #define alc885_macmini3_setup alc885_imac24_setup +/* Macbook Air 2,1 */ +static void alc885_mba21_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x14; + spec->autocfg.speaker_pins[0] = 0x18; +} + + + static void alc885_mbp3_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -9017,6 +9068,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC885_MACPRO] = "macpro", [ALC885_MB5] = "mb5", [ALC885_MACMINI3] = "macmini3", + [ALC885_MBA21] = "mba21", [ALC885_MBP3] = "mbp3", [ALC885_IMAC24] = "imac24", [ALC885_IMAC91] = "imac91", @@ -9252,6 +9304,18 @@ static struct alc_config_preset alc882_presets[] = { .input_mux = &alc882_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, }, + [ALC885_MBA21] = { + .mixers = { alc885_mba21_mixer }, + .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs }, + .num_dacs = 2, + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mba21_ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes), + .input_mux = &alc882_capture_source, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc885_mba21_setup, + .init_hook = alc_automute_amp, + }, [ALC885_MBP3] = { .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, .init_verbs = { alc885_mbp3_init_verbs, -- cgit v1.2.2 From dd2b4a7abf82d88261f8f98e1361388a7db2ffe4 Mon Sep 17 00:00:00 2001 From: "Zhang, Rui" Date: Wed, 24 Feb 2010 09:38:49 +0800 Subject: ALSA: hda - remove unnecessary msleep on power state transitions This will save ~15ms boot time. The first 10ms sleep was introduced in commit d2595d86e5 for (buggy) Cxt codecs, so better to limit the sleep to the problem hardware. For the second 10ms sleep, the HDA spec says: Power State[1:0]: 00: Node Power state (D0) is fully on. 01: Node Power state (D1) allows for (does not require) the lowest possible power consuming state from which it can return to the "fully on" state (D0) within 10 ms, excepting analog pass through circuits (e.g., CD analog playback) which must remain fully on. 10: Node Power state (D2) allows for (does not require) the lowest possible power consuming state from which it can return to the "fully on" state (D0) within 10 ms. For modems, this is the "wake on ring" power state. 11: Node Power state (D3) allows for (does not require) lowest possible power consuming state under software control. Note that any low power state set by software must retain sufficient operational capability to properly respond to subsequent software Power State command. So 10ms is actually the max wait time. It should be safe to remove/reduce it and rely on the loop of 1ms-sleeps. CC: Marc Boucher CC: Arjan van de Ven Signed-off-by: Zhang Rui Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 98767df4f03a..76d3c4c049db 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2767,7 +2767,8 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); /* partial workaround for "azx_get_response timeout" */ - if (power_state == AC_PWRST_D0) + if (power_state == AC_PWRST_D0 && + (codec->vendor_id & 0xffff0000) == 0x14f10000) msleep(10); nid = codec->start_nid; @@ -2801,7 +2802,6 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, if (power_state == AC_PWRST_D0) { unsigned long end_time; int state; - msleep(10); /* wait until the codec reachs to D0 */ end_time = jiffies + msecs_to_jiffies(500); do { -- cgit v1.2.2 From 6227cdced0328b0c4322c3170a727af5249393ce Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 25 Feb 2010 08:36:52 +0100 Subject: ALSA: hda - Add ALC670 codec support - Fixed alc_subsystem_id( ) typo and add new function. - !(ass & 0x100000)) ==> Delete this check. It is unnecessary check. - Add porti - ALC670 support Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5382872eba1f..220a49ff2179 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1254,7 +1254,7 @@ static void alc_init_auto_mic(struct hda_codec *codec) */ static int alc_subsystem_id(struct hda_codec *codec, hda_nid_t porta, hda_nid_t porte, - hda_nid_t portd) + hda_nid_t portd, hda_nid_t porti) { unsigned int ass, tmp, i; unsigned nid; @@ -1280,7 +1280,7 @@ static int alc_subsystem_id(struct hda_codec *codec, snd_printd("realtek: No valid SSID, " "checking pincfg 0x%08x for NID 0x%x\n", ass, nid); - if (!(ass & 1) && !(ass & 0x100000)) + if (!(ass & 1)) return 0; if ((ass >> 30) != 1) /* no physical connection */ return 0; @@ -1340,6 +1340,8 @@ do_sku: nid = porte; else if (tmp == 2) nid = portd; + else if (tmp == 3) + nid = porti; else return 1; for (i = 0; i < spec->autocfg.line_outs; i++) @@ -1354,9 +1356,10 @@ do_sku: } static void alc_ssid_check(struct hda_codec *codec, - hda_nid_t porta, hda_nid_t porte, hda_nid_t portd) + hda_nid_t porta, hda_nid_t porte, + hda_nid_t portd, hda_nid_t porti) { - if (!alc_subsystem_id(codec, porta, porte, portd)) { + if (!alc_subsystem_id(codec, porta, porte, portd, porti)) { struct alc_spec *spec = codec->spec; snd_printd("realtek: " "Enable default setup for auto mode as fallback\n"); @@ -4859,7 +4862,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; } @@ -6393,7 +6396,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - alc_ssid_check(codec, 0x10, 0x15, 0x0f); + alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0); return 1; } @@ -10224,7 +10227,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); err = alc_auto_add_mic_boost(codec); if (err < 0) @@ -11782,7 +11785,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - alc_ssid_check(codec, 0x15, 0x14, 0x1b); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; } @@ -12733,7 +12736,6 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, dac = 0x02; break; case 0x15: - case 0x21: dac = 0x03; break; default: @@ -12954,7 +12956,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; } @@ -13845,11 +13847,11 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { add_verb(spec, alc269vb_init_verbs); real_capsrc_nids = alc269vb_capsrc_nids[0]; - alc_ssid_check(codec, 0x21, 0x1b, 0x14); + alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); } else { add_verb(spec, alc269_init_verbs); real_capsrc_nids = alc269_capsrc_nids[0]; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); } spec->num_mux_defs = 1; @@ -15013,7 +15015,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); set_capture_mixer(codec); - alc_ssid_check(codec, 0x0e, 0x0f, 0x0b); + alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0); return 1; } @@ -15904,7 +15906,7 @@ static struct alc_config_preset alc861vd_presets[] = { static int alc861vd_auto_create_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { - return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x22, 0); + return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0); } @@ -16140,7 +16142,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; } @@ -17627,6 +17629,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", ALC662_3ST_6ch_DIG), + SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), @@ -18257,7 +18260,11 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - alc_ssid_check(codec, 0x15, 0x1b, 0x14); + if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || + codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670) + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21); + else + alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); return 1; } @@ -18407,6 +18414,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = { .patch = patch_alc662 }, { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 }, { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 }, + { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, -- cgit v1.2.2 From 61c2d2b5e7241d4410ab8227ef4f76c1aba8210b Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 25 Feb 2010 08:49:06 +0100 Subject: ALSA: hda - Add/fix ALC269 FSC and Quanta models Specify proper quirk models for FSC and Quanta machines with ALC269 codec. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 220a49ff2179..e8cbe216e912 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13946,8 +13946,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = { ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), + SND_PCI_QUIRK(0x104d, 0x9071, "SONY XTB", ALC269_DMIC), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), + SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), + SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), + SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC), + SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC), + SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC), + SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC), {} }; -- cgit v1.2.2 From 20645d70bdcdcc29b1b92011780d233008a8adcf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 2 Mar 2010 11:14:01 +0100 Subject: ALSA: hda - Add missing hp_pins definitions for ALC269 quirks In 2.6.33 ACL269 unsol event handler was changed to look up the pre-defined pins, but the headphone pins aren't defined properly in each quirk. This patch adds the missing definitions, and fixes the speaker auto-mute regression on some ASUS (and possibly other) laptops. Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e8cbe216e912..b9f4689ccd9a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13561,6 +13561,8 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec, static void alc269_quanta_fl1_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x19; @@ -13656,6 +13658,8 @@ static void alc269_laptop_unsol_event(struct hda_codec *codec, static void alc269_laptop_dmic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x12; @@ -13666,6 +13670,8 @@ static void alc269_laptop_dmic_setup(struct hda_codec *codec) static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x12; @@ -13676,6 +13682,8 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) static void alc269_laptop_amic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x19; -- cgit v1.2.2 From 28aedaf7bf6e4b629aea333978e8bb440bd1eb4f Mon Sep 17 00:00:00 2001 From: Norberto Lopes Date: Sun, 28 Feb 2010 20:16:53 +0100 Subject: ALSA: sound/pci/hda/hda_codec.c: various coding style fixes Signed-off-by: Norberto Lopes Acked-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 69 ++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 76d3c4c049db..5bd7cf45f3a5 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -978,8 +978,9 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, * * Returns 0 if successful, or a negative error code. */ -int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - struct hda_codec **codecp) +int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, + unsigned int codec_addr, + struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -1186,7 +1187,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); */ /* FIXME: more better hash key? */ -#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) +#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) #define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) #define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) @@ -1356,7 +1357,8 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) if (!codec->no_trigger_sense) { pincap = snd_hda_query_pin_caps(codec, nid); if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */ - snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); + snd_hda_codec_read(codec, nid, 0, + AC_VERB_SET_PIN_SENSE, 0); } return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); @@ -1372,8 +1374,8 @@ EXPORT_SYMBOL_HDA(snd_hda_pin_sense); */ int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) { - u32 sense = snd_hda_pin_sense(codec, nid); - return !!(sense & AC_PINSENSE_PRESENCE); + u32 sense = snd_hda_pin_sense(codec, nid); + return !!(sense & AC_PINSENSE_PRESENCE); } EXPORT_SYMBOL_HDA(snd_hda_jack_detect); @@ -1952,7 +1954,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; - + for (s = slaves; *s; s++) { struct snd_kcontrol *sctl; int i = 0; @@ -2439,27 +2441,27 @@ static struct snd_kcontrol_new dig_mixes[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_cmask_get, }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_pmask_get, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_default_get, .put = snd_hda_spdif_default_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), .info = snd_hda_spdif_out_switch_info, .get = snd_hda_spdif_out_switch_get, .put = snd_hda_spdif_out_switch_put, @@ -2610,7 +2612,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, static struct snd_kcontrol_new dig_in_ctls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH), .info = snd_hda_spdif_in_switch_info, .get = snd_hda_spdif_in_switch_get, .put = snd_hda_spdif_in_switch_put, @@ -2618,7 +2620,7 @@ static struct snd_kcontrol_new dig_in_ctls[] = { { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), .info = snd_hda_spdif_mask_info, .get = snd_hda_spdif_in_status_get, }, @@ -2883,7 +2885,7 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) int err = snd_hda_codec_build_controls(codec); if (err < 0) { printk(KERN_ERR "hda_codec: cannot build controls" - "for #%d (error %d)\n", codec->addr, err); + "for #%d (error %d)\n", codec->addr, err); err = snd_hda_codec_reset(codec); if (err < 0) { printk(KERN_ERR @@ -2979,8 +2981,12 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, val |= channels - 1; switch (snd_pcm_format_width(format)) { - case 8: val |= 0x00; break; - case 16: val |= 0x10; break; + case 8: + val |= 0x00; + break; + case 16: + val |= 0x10; + break; case 20: case 24: case 32: @@ -3298,7 +3304,8 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) return audio_idx[type][i]; - snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]); + snd_printk(KERN_WARNING "Too many %s devices\n", + snd_hda_pcm_type_name[type]); return -EAGAIN; } @@ -3336,7 +3343,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) err = codec->patch_ops.build_pcms(codec); if (err < 0) { printk(KERN_ERR "hda_codec: cannot build PCMs" - "for #%d (error %d)\n", codec->addr, err); + "for #%d (error %d)\n", codec->addr, err); err = snd_hda_codec_reset(codec); if (err < 0) { printk(KERN_ERR @@ -3466,8 +3473,8 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_config); /** * snd_hda_check_board_codec_sid_config - compare the current codec - subsystem ID with the - config table + subsystem ID with the + config table This is important for Gateway notebooks with SB450 HDA Audio where the vendor ID of the PCI device is: @@ -3607,7 +3614,7 @@ void snd_hda_update_power_acct(struct hda_codec *codec) * * Increment the power-up counter and power up the hardware really when * not turned on yet. - */ + */ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; @@ -3636,7 +3643,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); * * Decrement the power-up counter and schedules the power-off work if * the counter rearches to zero. - */ + */ void snd_hda_power_down(struct hda_codec *codec) { --codec->power_count; @@ -3662,7 +3669,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_down); * * This function is supposed to be set or called from the check_power_status * patch ops. - */ + */ int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, hda_nid_t nid) @@ -3830,7 +3837,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid, { /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) - set_dig_out_convert(codec, nid, + set_dig_out_convert(codec, nid, codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff, -1); snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); @@ -4089,13 +4096,13 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list) /* * Sort an associated group of pins according to their sequence numbers. */ -static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences, +static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, int num_pins) { int i, j; short seq; hda_nid_t nid; - + for (i = 0; i < num_pins; i++) { for (j = i + 1; j < num_pins; j++) { if (sequences[i] > sequences[j]) { @@ -4123,7 +4130,7 @@ static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences, * is detected, one of speaker of HP pins is assigned as the primary * output, i.e. to line_out_pins[0]. So, line_outs is always positive * if any analog output exists. - * + * * The analog input pins are assigned to input_pins array. * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, * respectively. @@ -4186,9 +4193,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, case AC_JACK_SPEAKER: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); - if (! assoc) + if (!assoc) continue; - if (! assoc_speaker) + if (!assoc_speaker) assoc_speaker = assoc; else if (assoc_speaker != assoc) continue; @@ -4286,7 +4293,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->speaker_outs); sort_pins_by_sequence(cfg->hp_pins, sequences_hp, cfg->hp_outs); - + /* if we have only one mic, make it AUTO_PIN_MIC */ if (!cfg->input_pins[AUTO_PIN_MIC] && cfg->input_pins[AUTO_PIN_FRONT_MIC]) { @@ -4436,7 +4443,7 @@ EXPORT_SYMBOL_HDA(snd_hda_resume); /** * snd_array_new - get a new element from the given array * @array: the array object - * + * * Get a new element from the given array. If it exceeds the * pre-allocated array size, re-allocate the array. * -- cgit v1.2.2 From faf4eb23d5fcb9a4728766a1e7bce9c6f2b43bd8 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 3 Mar 2010 09:16:18 +0100 Subject: ALSA: oxygen: change || to && In the original code the condition was always true (hopefully) because WM8776_HPLVOL is zero. Signed-off-by: Dan Carpenter Signed-off-by: Clemens Ladisch Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/oxygen/xonar_wm87x6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 7754db166d9e..dbc4b89d74e4 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -68,7 +68,7 @@ static void wm8776_write(struct oxygen *chip, OXYGEN_SPI_CEN_LATCH_CLOCK_LO, (reg << 9) | value); if (reg < ARRAY_SIZE(data->wm8776_regs)) { - if (reg >= WM8776_HPLVOL || reg <= WM8776_DACMASTER) + if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER) value &= ~WM8776_UPDATE; data->wm8776_regs[reg] = value; } -- cgit v1.2.2 From 282572b5ab99cf27073210ca60b80dd085e1a469 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Mar 2010 10:13:49 +0300 Subject: ALSA: riptide: clean up while loop If getpaths() returned an odd number this would be a buffer under-run and an endless loop. It turns out that getpaths() can only return even numbers, but let's make it easy for people auditing code. With the new code you don't need to look at getpaths(). This silences a smatch warning. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/riptide/riptide.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 960a227eb653..ad4462677615 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1974,9 +1974,9 @@ snd_riptide_proc_read(struct snd_info_entry *entry, } snd_iprintf(buffer, "Paths:\n"); i = getpaths(cif, p); - while (i--) { - snd_iprintf(buffer, "%x->%x ", p[i - 1], p[i]); - i--; + while (i >= 2) { + i -= 2; + snd_iprintf(buffer, "%x->%x ", p[i], p[i + 1]); } snd_iprintf(buffer, "\n"); } -- cgit v1.2.2 From 7445dfc159f90b4bc82fd7d898b53d74520e2f83 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Wed, 3 Mar 2010 15:05:53 +0800 Subject: ALSA: hda - Support max codecs to 8 for nvidia hda controller Support max codecs to 8 for nvidia hda controller. Change AZX_MAX_CODECS to 8, and add "#define AZX_DEFAULT_CODECS 4" for default driver. Set azx_max_codecs to 8 for nvidia controller. Signed-off-by: Wei Ni Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1adac8cc9592..b1047570e78d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -267,7 +267,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define RIRB_INT_MASK 0x05 /* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 4 +#define AZX_MAX_CODECS 8 +#define AZX_DEFAULT_CODECS 4 #define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) /* SD_CTL bits */ @@ -1367,6 +1368,7 @@ static void azx_bus_reset(struct hda_bus *bus) /* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { + [AZX_DRIVER_NVIDIA] = 8, [AZX_DRIVER_TERA] = 1, }; @@ -1399,7 +1401,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) codecs = 0; max_slots = azx_max_codecs[chip->driver_type]; if (!max_slots) - max_slots = AZX_MAX_CODECS; + max_slots = AZX_DEFAULT_CODECS; /* First try to probe all given codec slots */ for (c = 0; c < max_slots; c++) { -- cgit v1.2.2 From 25045705d4053925a617ed71c5e4b6888e468765 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Wed, 3 Mar 2010 15:11:40 +0800 Subject: ALSA: hda - Support NVIDIA MCP89 and GT21x hdmi audio Support nvidia MCP89 and GT21x 8ch hdmi audio. Add some eld support. Signed-off-by: Wei Ni Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 2 +- sound/pci/hda/Makefile | 2 +- sound/pci/hda/patch_nvhdmi.c | 1038 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 990 insertions(+), 52 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 556cff937be7..567348b05b5a 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -157,7 +157,7 @@ config SND_HDA_CODEC_INTELHDMI config SND_HDA_ELD def_bool y - depends on SND_HDA_CODEC_INTELHDMI + depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 315a1c4f8998..199f4405b3ad 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -17,7 +17,7 @@ snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o -snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o +snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o hda_eld.o snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o # common driver diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 6afdab09bab7..1c774f942407 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -32,10 +32,11 @@ /* define below to restrict the supported rates and formats */ /* #define LIMITED_RATE_FMT_SUPPORT */ -struct nvhdmi_spec { - struct hda_multi_out multiout; - - struct hda_pcm pcm_rec; +enum HDACodec { + HDA_CODEC_NVIDIA_MCP7X, + HDA_CODEC_NVIDIA_MCP89, + HDA_CODEC_NVIDIA_GT21X, + HDA_CODEC_INVALID }; #define Nv_VERB_SET_Channel_Allocation 0xF79 @@ -43,15 +44,18 @@ struct nvhdmi_spec { #define Nv_VERB_SET_Audio_Protection_On 0xF98 #define Nv_VERB_SET_Audio_Protection_Off 0xF99 -#define Nv_Master_Convert_nid 0x04 -#define Nv_Master_Pin_nid 0x05 +#define nvhdmi_master_con_nid_7x 0x04 +#define nvhdmi_master_pin_nid_7x 0x05 -static hda_nid_t nvhdmi_convert_nids[4] = { +#define nvhdmi_master_con_nid_89 0x04 +#define nvhdmi_master_pin_nid_89 0x05 + +static hda_nid_t nvhdmi_con_nids_7x[4] = { /*front, rear, clfe, rear_surr */ 0x6, 0x8, 0xa, 0xc, }; -static struct hda_verb nvhdmi_basic_init[] = { +static struct hda_verb nvhdmi_basic_init_7x[] = { /* set audio protect on */ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1}, /* enable digital output on pin widget */ @@ -79,6 +83,796 @@ static struct hda_verb nvhdmi_basic_init[] = { (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) #endif +#define NVIDIA_89_HDMI_CVTS 1 +#define NVIDIA_89_HDMI_PINS 1 + +static char *nvhdmi_pcm_names[NVIDIA_89_HDMI_CVTS] = { + "NVIDIA HDMI", +}; + +struct nvhdmi_spec { + int num_cvts; + int num_pins; + hda_nid_t cvt[NVIDIA_89_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[NVIDIA_89_HDMI_PINS+1]; /* audio sinks */ + hda_nid_t pin_cvt[NVIDIA_89_HDMI_PINS+1]; + struct hda_pcm pcm_rec[NVIDIA_89_HDMI_CVTS]; + struct hdmi_eld sink_eld[NVIDIA_89_HDMI_PINS]; + struct hda_multi_out multiout; + unsigned int codec_type; +}; + +struct hdmi_audio_infoframe { + u8 type; /* 0x84 */ + u8 ver; /* 0x01 */ + u8 len; /* 0x0a */ + + u8 checksum; /* PB0 */ + u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x32, 0x23, 0x64, 0x75, 0x46, 0x57 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_setup_channel_allocation(). + */ +static struct cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + +/* + * HDA/HDMI auto parsing + */ + +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + +static int nvhdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct nvhdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, + HDA_MAX_CONNECTIONS); + if (conn_len > 1) + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + else + curr = 0; + + index = hda_node_index(spec->pin, pin_nid); + if (index < 0) + return -EINVAL; + + spec->pin_cvt[index] = conn_list[curr]; + + return 0; +} + +static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + snd_hdmi_show_eld(eld); +} + +static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + int present = snd_hda_pin_sense(codec, pin_nid); + + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + + if (present & AC_PINSENSE_ELDV) + hdmi_get_show_eld(codec, pin_nid, eld); +} + +static int nvhdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct nvhdmi_spec *spec = codec->spec; + + if (spec->num_pins >= NVIDIA_89_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d \n", pin_nid); + return -EINVAL; + } + + hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return nvhdmi_read_pin_conn(codec, pin_nid); +} + +static int nvhdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct nvhdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= NVIDIA_89_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d \n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + + +static int nvhdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (nvhdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) + continue; + if (nvhdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + /* + * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event + * can be lost and presence sense verb will become inaccurate if the + * HDA link is powered off at hot plug or hw initialization time. + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & + AC_PWRST_EPSS)) + codec->bus->power_keep_link_on = 1; +#endif + + return 0; +} + +/* + * HDMI routines + */ + +#ifdef BE_PARANOID +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int *packet_index, int *byte_index) +{ + int val; + + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); + + *packet_index = val >> 5; + *byte_index = val & 0x1f; +} +#endif + +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int packet_index, int byte_index) +{ + int val; + + val = (packet_index << 5) | (byte_index & 0x1f); + + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); +} + +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, + unsigned char val) +{ + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); +} + +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) +{ + /* Unmute */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + /* Enable pin out */ + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +} + +/* + * Enable Audio InfoFrame Transmission + */ +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_BEST); +} + +/* + * Disable Audio InfoFrame Transmission + */ +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_DISABLE); +} + +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) +{ + return 1 + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, nid)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + +static void hdmi_debug_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int slot; + + for (i = 0; i < 8; i++) { + slot = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, i); + printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", + slot >> 4, slot & 0xf); + } +#endif +} + + +/* + * Audio InfoFrame routines + */ + +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int size; + + size = snd_hdmi_get_eld_size(codec, pin_nid); + printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); + + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); + } +#endif +} + +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef BE_PARANOID + int i, j; + int size; + int pi, bi; + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + if (size == 0) + continue; + + hdmi_set_dip_index(codec, pin_nid, i, 0x0); + for (j = 1; j < 1000; j++) { + hdmi_write_dip_byte(codec, pin_nid, 0x0); + hdmi_get_dip_index(codec, pin_nid, &pi, &bi); + if (pi != i) + snd_printd(KERN_INFO "dip index %d: %d != %d\n", + bi, pi, i); + if (bi == 0) /* byte index wrapped around */ + break; + } + snd_printd(KERN_INFO + "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", + i, size, j); + } +#endif +} + +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) +{ + ai->checksum = 0; +} + +static void hdmi_fill_audio_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + int i; + + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ + + hdmi_checksum_audio_infoframe(ai); + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) + hdmi_write_dip_byte(codec, pin_nid, bytes[i]); +} + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) +{ + struct nvhdmi_spec *spec = codec->spec; + struct hdmi_eld *eld; + int i; + int spk_mask = 0; + int channels = 1 + (ai->CC02_CT47 & 0x7); + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + i = hda_node_index(spec->pin_cvt, nid); + if (i < 0) + return 0; + eld = &spec->sink_eld[i]; + + /* + * HDMI sink's ELD info cannot always be retrieved for now, e.g. + * in console or for audio devices. Assume the highest speakers + * configuration, to _not_ prohibit multi-channel audio playback. + */ + if (!eld->spk_alloc) + eld->spk_alloc = 0xffff; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (eld->spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ai->CA = channel_allocations[i].ca_index; + break; + } + } + + snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); + snd_printdd(KERN_INFO + "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ai->CA, channels, buf); + + return ai->CA; +} + +static void hdmi_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + int i; + int ca = ai->CA; + int err; + + if (hdmi_channel_mapping[ca][1] == 0) { + for (i = 0; i < channel_allocations[ca].channels; i++) + hdmi_channel_mapping[ca][i] = i | (i << 4); + for (; i < 8; i++) + hdmi_channel_mapping[ca][i] = 0xf | (i << 4); + } + + for (i = 0; i < 8; i++) { + err = snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + hdmi_channel_mapping[ca][i]); + if (err) { + snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); + break; + } + } + + hdmi_debug_channel_mapping(codec, pin_nid); +} + +static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 val; + int i; + + if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) + != AC_DIPXMIT_BEST) + return false; + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) { + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_DATA, 0); + if (val != bytes[i]) + return false; + } + + return true; +} + +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, + struct snd_pcm_substream *substream) +{ + struct nvhdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; + struct hdmi_audio_infoframe ai = { + .type = 0x84, + .ver = 0x01, + .len = 0x0a, + .CC02_CT47 = substream->runtime->channels - 1, + }; + + hdmi_setup_channel_allocation(codec, nid, &ai); + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (!spec->sink_eld[i].monitor_present) + continue; + + pin_nid = spec->pin[i]; + if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + hdmi_setup_channel_mapping(codec, pin_nid, &ai); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } + } +} + +/* + * Unsolicited events + */ + +static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + struct nvhdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int pind = !!(res & AC_UNSOL_RES_PD); + int eldv = !!(res & AC_UNSOL_RES_ELDV); + int index; + + printk(KERN_INFO + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_eld[index].monitor_present = pind; + spec->sink_eld[index].eld_valid = eldv; + + if (eldv) { + spec->sink_eld[index].monitor_present = 1; + hdmi_get_show_eld(codec, spec->pin[index], + &spec->sink_eld[index]); + /* TODO: do real things about ELD */ + } +} + +static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); + int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); + + printk(KERN_INFO + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, + subtag, + cp_state, + cp_ready); + + /* TODO */ + if (cp_state) + ; + if (cp_ready) + ; +} + +static void nvhdmi_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct nvhdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + + if (hda_node_index(spec->pin, tag) < 0) { + snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); + return; + } + + if (subtag == 0) + hdmi_intrinsic_event(codec, res); + else + hdmi_non_intrinsic_event(codec, res); +} + +/* + * Callbacks + */ + +static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, int format) +{ + int tag; + int fmt; + + tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; + fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); + + snd_printdd("hdmi_setup_stream: " + "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", + nid, + tag == stream_tag ? "" : "new-", + stream_tag, + fmt == format ? "" : "new-", + format); + + if (tag != stream_tag) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + stream_tag << 4); + if (fmt != format) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); +} + /* * Controls */ @@ -86,20 +880,58 @@ static int nvhdmi_build_controls(struct hda_codec *codec) { struct nvhdmi_spec *spec = codec->spec; int err; + int i; - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); - if (err < 0) - return err; + if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) + || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { + for (i = 0; i < codec->num_pcms; i++) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->cvt[i]); + if (err < 0) + return err; + } + } else { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + } return 0; } static int nvhdmi_init(struct hda_codec *codec) { - snd_hda_sequence_write(codec, nvhdmi_basic_init); + struct nvhdmi_spec *spec = codec->spec; + int i; + if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) + || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { + for (i = 0; spec->pin[i]; i++) { + hdmi_enable_output(codec, spec->pin[i]); + snd_hda_codec_write(codec, spec->pin[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | spec->pin[i]); + } + } else { + snd_hda_sequence_write(codec, nvhdmi_basic_init_7x); + } return 0; } +static void nvhdmi_free(struct hda_codec *codec) +{ + struct nvhdmi_spec *spec = codec->spec; + int i; + + if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) + || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_free(codec, &spec->sink_eld[i]); + } + + kfree(spec); +} + /* * Digital out */ @@ -111,21 +943,21 @@ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_open(codec, &spec->multiout); } -static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo, +static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct nvhdmi_spec *spec = codec->spec; int i; - snd_hda_codec_write(codec, Nv_Master_Convert_nid, + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_CHANNEL_STREAMID, 0); for (i = 0; i < 4; i++) { /* set the stream id */ - snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_CHANNEL_STREAMID, 0); /* set the stream format */ - snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0, + snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_STREAM_FORMAT, 0); } @@ -140,6 +972,21 @@ static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_dig_close(codec, &spec->multiout); } +static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + hdmi_set_channel_count(codec, hinfo->nid, + substream->runtime->channels); + + hdmi_setup_audio_infoframe(codec, hinfo->nid, substream); + + hdmi_setup_stream(codec, hinfo->nid, stream_tag, format); + return 0; +} + static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -181,29 +1028,29 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, - Nv_Master_Convert_nid, + nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ - snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0); /* set the stream format */ - snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0, + snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_STREAM_FORMAT, format); /* turn on again (if needed) */ /* enable and set the channel status audio/data flag */ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, - Nv_Master_Convert_nid, + nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & 0xff); snd_hda_codec_write(codec, - Nv_Master_Convert_nid, + nvhdmi_master_con_nid_7x, 0, AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); } @@ -220,19 +1067,19 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, - nvhdmi_convert_nids[i], + nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); /* set the stream id */ snd_hda_codec_write(codec, - nvhdmi_convert_nids[i], + nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | channel_id); /* set the stream format */ snd_hda_codec_write(codec, - nvhdmi_convert_nids[i], + nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_STREAM_FORMAT, format); @@ -241,12 +1088,12 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) { snd_hda_codec_write(codec, - nvhdmi_convert_nids[i], + nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_1, codec->spdif_ctls & 0xff); snd_hda_codec_write(codec, - nvhdmi_convert_nids[i], + nvhdmi_con_nids_7x[i], 0, AC_VERB_SET_DIGI_CONVERT_2, dataDCC2); } @@ -261,6 +1108,13 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo, return 0; } +static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + return 0; +} + static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -272,17 +1126,29 @@ static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo, format, substream); } -static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = { +static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = { + .substreams = 1, + .channels_min = 2, + .rates = SUPPORTED_RATES, + .maxbps = SUPPORTED_MAXBPS, + .formats = SUPPORTED_FORMATS, + .ops = { + .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89, + .cleanup = nvhdmi_playback_pcm_cleanup, + }, +}; + +static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = { .substreams = 1, .channels_min = 2, .channels_max = 8, - .nid = Nv_Master_Convert_nid, + .nid = nvhdmi_master_con_nid_7x, .rates = SUPPORTED_RATES, .maxbps = SUPPORTED_MAXBPS, .formats = SUPPORTED_FORMATS, .ops = { .open = nvhdmi_dig_playback_pcm_open, - .close = nvhdmi_dig_playback_pcm_close_8ch, + .close = nvhdmi_dig_playback_pcm_close_8ch_7x, .prepare = nvhdmi_dig_playback_pcm_prepare_8ch }, }; @@ -291,7 +1157,7 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { .substreams = 1, .channels_min = 2, .channels_max = 2, - .nid = Nv_Master_Convert_nid, + .nid = nvhdmi_master_con_nid_7x, .rates = SUPPORTED_RATES, .maxbps = SUPPORTED_MAXBPS, .formats = SUPPORTED_FORMATS, @@ -302,10 +1168,36 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { }, }; -static int nvhdmi_build_pcms_8ch(struct hda_codec *codec) +static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec) +{ + struct nvhdmi_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + int i; + + codec->num_pcms = spec->num_cvts; + codec->pcm_info = info; + + for (i = 0; i < codec->num_pcms; i++, info++) { + unsigned int chans; + + chans = get_wcaps(codec, spec->cvt[i]); + chans = get_wcaps_channels(chans); + + info->name = nvhdmi_pcm_names[i]; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] + = nvhdmi_pcm_digital_playback_8ch_89; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; + } + + return 0; +} + +static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec) { struct nvhdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; codec->pcm_info = info; @@ -313,7 +1205,7 @@ static int nvhdmi_build_pcms_8ch(struct hda_codec *codec) info->name = "NVIDIA HDMI"; info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] - = nvhdmi_pcm_digital_playback_8ch; + = nvhdmi_pcm_digital_playback_8ch_7x; return 0; } @@ -321,7 +1213,7 @@ static int nvhdmi_build_pcms_8ch(struct hda_codec *codec) static int nvhdmi_build_pcms_2ch(struct hda_codec *codec) { struct nvhdmi_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; codec->pcm_info = info; @@ -334,14 +1226,17 @@ static int nvhdmi_build_pcms_2ch(struct hda_codec *codec) return 0; } -static void nvhdmi_free(struct hda_codec *codec) -{ - kfree(codec->spec); -} +static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = { + .build_controls = nvhdmi_build_controls, + .build_pcms = nvhdmi_build_pcms_8ch_89, + .init = nvhdmi_init, + .free = nvhdmi_free, + .unsol_event = nvhdmi_unsol_event, +}; -static struct hda_codec_ops nvhdmi_patch_ops_8ch = { +static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { .build_controls = nvhdmi_build_controls, - .build_pcms = nvhdmi_build_pcms_8ch, + .build_pcms = nvhdmi_build_pcms_8ch_7x, .init = nvhdmi_init, .free = nvhdmi_free, }; @@ -353,7 +1248,34 @@ static struct hda_codec_ops nvhdmi_patch_ops_2ch = { .free = nvhdmi_free, }; -static int patch_nvhdmi_8ch(struct hda_codec *codec) +static int patch_nvhdmi_8ch_89(struct hda_codec *codec) +{ + struct nvhdmi_spec *spec; + int i; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->codec_type = HDA_CODEC_NVIDIA_MCP89; + + if (nvhdmi_parse_codec(codec) < 0) { + codec->spec = NULL; + kfree(spec); + return -EINVAL; + } + codec->patch_ops = nvhdmi_patch_ops_8ch_89; + + for (i = 0; i < spec->num_pins; i++) + snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i); + + init_channel_allocations(); + + return 0; +} + +static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) { struct nvhdmi_spec *spec; @@ -365,9 +1287,10 @@ static int patch_nvhdmi_8ch(struct hda_codec *codec) spec->multiout.num_dacs = 0; /* no analog */ spec->multiout.max_channels = 8; - spec->multiout.dig_out_nid = Nv_Master_Convert_nid; + spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; + spec->codec_type = HDA_CODEC_NVIDIA_MCP7X; - codec->patch_ops = nvhdmi_patch_ops_8ch; + codec->patch_ops = nvhdmi_patch_ops_8ch_7x; return 0; } @@ -384,7 +1307,8 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) spec->multiout.num_dacs = 0; /* no analog */ spec->multiout.max_channels = 2; - spec->multiout.dig_out_nid = Nv_Master_Convert_nid; + spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x; + spec->codec_type = HDA_CODEC_NVIDIA_MCP7X; codec->patch_ops = nvhdmi_patch_ops_2ch; @@ -395,13 +1319,24 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) * patch entries */ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, - { .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, - { .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, - { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch }, - { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch }, { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, + { .id = 0x10de0002, .name = "MCP77/78 HDMI", + .patch = patch_nvhdmi_8ch_7x }, + { .id = 0x10de0003, .name = "MCP77/78 HDMI", + .patch = patch_nvhdmi_8ch_7x }, + { .id = 0x10de0005, .name = "MCP77/78 HDMI", + .patch = patch_nvhdmi_8ch_7x }, + { .id = 0x10de0006, .name = "MCP77/78 HDMI", + .patch = patch_nvhdmi_8ch_7x }, + { .id = 0x10de0007, .name = "MCP79/7A HDMI", + .patch = patch_nvhdmi_8ch_7x }, + { .id = 0x10de000c, .name = "MCP89 HDMI", + .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de000b, .name = "GT21x HDMI", + .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de000d, .name = "GT240 HDMI", + .patch = patch_nvhdmi_8ch_89 }, {} /* terminator */ }; @@ -412,9 +1347,12 @@ MODULE_ALIAS("snd-hda-codec-id:10de0006"); MODULE_ALIAS("snd-hda-codec-id:10de0007"); MODULE_ALIAS("snd-hda-codec-id:10de0067"); MODULE_ALIAS("snd-hda-codec-id:10de8001"); +MODULE_ALIAS("snd-hda-codec-id:10de000c"); +MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000d"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec"); +MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec"); static struct hda_codec_preset_list nvhdmi_list = { .preset = snd_hda_preset_nvhdmi, -- cgit v1.2.2 From dd74b4653597d1d321efa13935cb029b4d819343 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Mar 2010 16:05:24 +0100 Subject: ALSA: hda - Build hda_eld into snd-hda-codec module Now two modules require hda_eld.o, so we need to put it to the common place instead of building into two individual modules. Signed-off-by: Takashi Iwai --- sound/pci/hda/Makefile | 6 +++--- sound/pci/hda/hda_eld.c | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 199f4405b3ad..24bc195b02da 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -3,7 +3,7 @@ snd-hda-intel-objs := hda_intel.o snd-hda-codec-y := hda_codec.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o -# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o +snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o @@ -17,8 +17,8 @@ snd-hda-codec-cirrus-objs := patch_cirrus.o snd-hda-codec-ca0110-objs := patch_ca0110.o snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o -snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o hda_eld.o -snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o +snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o +snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o # common driver obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 4228f2fe5956..dcd22446cfc7 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -331,6 +331,7 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, AC_DIPSIZE_ELD_BUF); } +EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size); int snd_hdmi_get_eld(struct hdmi_eld *eld, struct hda_codec *codec, hda_nid_t nid) @@ -366,6 +367,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, kfree(buf); return ret; } +EXPORT_SYMBOL_HDA(snd_hdmi_get_eld); static void hdmi_show_short_audio_desc(struct cea_sad *a) { @@ -404,6 +406,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) } buf[j] = '\0'; /* necessary when j == 0 */ } +EXPORT_SYMBOL_HDA(snd_print_channel_allocation); void snd_hdmi_show_eld(struct hdmi_eld *e) { @@ -422,6 +425,7 @@ void snd_hdmi_show_eld(struct hdmi_eld *e) for (i = 0; i < e->sad_count; i++) hdmi_show_short_audio_desc(e->sad + i); } +EXPORT_SYMBOL_HDA(snd_hdmi_show_eld); #ifdef CONFIG_PROC_FS @@ -580,6 +584,7 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, return 0; } +EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new); void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) { @@ -588,5 +593,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) eld->proc_entry = NULL; } } +EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free); #endif /* CONFIG_PROC_FS */ -- cgit v1.2.2 From 9919c7619c52d01e89103bca405cc3d4a2b1ac31 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 3 Mar 2010 18:24:26 -0500 Subject: ALSA: hda: Use LPIB for Dell Latitude 131L BugLink: https://launchpad.net/bugs/530346 The OR has verified that position_fix=1 is necessary to work around errors on his machine. Reported-by: Tom Louwrier Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index b1047570e78d..531a0b6a66c1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2268,6 +2268,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_LPIB), SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB), SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), -- cgit v1.2.2 From 0321b69569eadbc13242922925a4316754c5f744 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Fri, 5 Mar 2010 09:04:49 -0500 Subject: ALSA: hda: Use LPIB for a Biostar Microtech board BugLink: https://launchpad.net/bugs/523953 The OR has verified that position_fix=1 is necessary to work around errors on his machine. Reported-by: MMarking Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 531a0b6a66c1..c24bffa08c84 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2273,6 +2273,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), {} }; -- cgit v1.2.2 From d2db09b87eb7b547136d5d25ff1df06820e070bf Mon Sep 17 00:00:00 2001 From: Frederik Deweerdt Date: Fri, 5 Mar 2010 16:34:31 +0100 Subject: ALSA: hda: uninitialized variable fix Commit eaa9b3a748539651f50e3a234c8854e1b42a839a introduced the following uninitialized warning: sound/pci/hda/patch_realtek.c: In function 'set_capture_mixer': sound/pci/hda/patch_realtek.c:4928: warning: 'pin' is used uninitialized in this function sound/pci/hda/patch_realtek.c:4918: note: 'pin' was declared here It appears indeed that 'pin' needs to be initialized to 0. Signed-off-by: Frederik Deweerdt Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b9f4689ccd9a..5d2fbb87b871 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4915,7 +4915,7 @@ static void fixup_automic_adc(struct hda_codec *codec) static void fixup_single_adc(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin; + hda_nid_t pin = 0; int i; /* search for the input pin; there must be only one */ -- cgit v1.2.2 From 4193d13b2c2b694aa59e629e6daf6269d7922f13 Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Sat, 6 Mar 2010 21:06:46 +0100 Subject: ALSA: hda - Add ASRock mobo to MSI blacklist This avoids a lockup at boot. Signed-off-by: Michele Ballabio Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 94b444e6fed3..e37bffec749a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2358,6 +2358,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ + SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ {} }; -- cgit v1.2.2 From 079d88ccc374d2c1a850b8a83595ba4c907fb3df Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Mon, 8 Mar 2010 10:44:23 +0800 Subject: ALSA: hdmi - merge common code for intelhdmi and nvhdmi Create patch_hdmi.c to hold common code from intelhdmi and nvhdmi. For now the patch_hdmi.c file is simply included by patch_intelhdmi.c and patch_nvhdmi.c, and does not represent a real codec. There are no behavior changes to intelhdmi. However nvhdmi made several changes when copying code out of intelhdmi, which are all reverted in this patch. Wei Ni confirmed that the reverted code actually works fine. Tested-by: Wei Ni Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 845 ++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/patch_intelhdmi.c | 821 +------------------------------------- sound/pci/hda/patch_nvhdmi.c | 829 ++------------------------------------- 3 files changed, 882 insertions(+), 1613 deletions(-) create mode 100644 sound/pci/hda/patch_hdmi.c (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c new file mode 100644 index 000000000000..b2ab39670dda --- /dev/null +++ b/sound/pci/hda/patch_hdmi.c @@ -0,0 +1,845 @@ +/* + * + * patch_hdmi.c - routines for HDMI/DisplayPort codecs + * + * Copyright(c) 2008-2010 Intel Corporation. All rights reserved. + * + * Authors: + * Wu Fengguang + * + * Maintained by: + * Wu Fengguang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +struct hdmi_spec { + int num_cvts; + int num_pins; + hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */ + hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */ + + /* + * source connection for each pin + */ + hda_nid_t pin_cvt[MAX_HDMI_PINS+1]; + + /* + * HDMI sink attached to each pin + */ + struct hdmi_eld sink_eld[MAX_HDMI_PINS]; + + /* + * export one pcm per pipe + */ + struct hda_pcm pcm_rec[MAX_HDMI_CVTS]; + + /* + * nvhdmi specific + */ + struct hda_multi_out multiout; + unsigned int codec_type; +}; + + +struct hdmi_audio_infoframe { + u8 type; /* 0x84 */ + u8 ver; /* 0x01 */ + u8 len; /* 0x0a */ + + u8 checksum; /* PB0 */ + u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; + u8 reserved[5]; /* PB6 - PB10 */ +}; + +/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_setup_channel_allocation(). + */ +static struct cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + + +/* + * HDMI routines + */ + +static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) +{ + int i; + + for (i = 0; nids[i]; i++) + if (nids[i] == nid) + return i; + + snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); + return -EINVAL; +} + +static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + if (!snd_hdmi_get_eld(eld, codec, pin_nid)) + snd_hdmi_show_eld(eld); +} + +#ifdef BE_PARANOID +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int *packet_index, int *byte_index) +{ + int val; + + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_INDEX, 0); + + *packet_index = val >> 5; + *byte_index = val & 0x1f; +} +#endif + +static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, + int packet_index, int byte_index) +{ + int val; + + val = (packet_index << 5) | (byte_index & 0x1f); + + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); +} + +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, + unsigned char val) +{ + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); +} + +static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) +{ + /* Unmute */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + /* Enable pin out */ + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +} + +static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) +{ + return 1 + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hda_codec *codec, + hda_nid_t nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, nid)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + + +/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, + struct hdmi_audio_infoframe *ai) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld; + int i; + int spk_mask = 0; + int channels = 1 + (ai->CC02_CT47 & 0x7); + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + i = hda_node_index(spec->pin_cvt, nid); + if (i < 0) + return 0; + eld = &spec->sink_eld[i]; + + /* + * HDMI sink's ELD info cannot always be retrieved for now, e.g. + * in console or for audio devices. Assume the highest speakers + * configuration, to _not_ prohibit multi-channel audio playback. + */ + if (!eld->spk_alloc) + eld->spk_alloc = 0xffff; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (eld->spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ai->CA = channel_allocations[i].ca_index; + break; + } + } + + snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); + snd_printdd(KERN_INFO + "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ai->CA, channels, buf); + + return ai->CA; +} + +static void hdmi_debug_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int slot; + + for (i = 0; i < 8; i++) { + slot = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, i); + printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", + slot >> 4, slot & 0xf); + } +#endif +} + + +static void hdmi_setup_channel_mapping(struct hda_codec *codec, + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + int i; + int ca = ai->CA; + int err; + + if (hdmi_channel_mapping[ca][1] == 0) { + for (i = 0; i < channel_allocations[ca].channels; i++) + hdmi_channel_mapping[ca][i] = i | (i << 4); + for (; i < 8; i++) + hdmi_channel_mapping[ca][i] = 0xf | (i << 4); + } + + for (i = 0; i < 8; i++) { + err = snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + hdmi_channel_mapping[ca][i]); + if (err) { + snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); + break; + } + } + + hdmi_debug_channel_mapping(codec, pin_nid); +} + + +/* + * Audio InfoFrame routines + */ + +/* + * Enable Audio InfoFrame Transmission + */ +static void hdmi_start_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_BEST); +} + +/* + * Disable Audio InfoFrame Transmission + */ +static void hdmi_stop_infoframe_trans(struct hda_codec *codec, + hda_nid_t pin_nid) +{ + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, + AC_DIPXMIT_DISABLE); +} + +static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int size; + + size = snd_hdmi_get_eld_size(codec, pin_nid); + printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); + + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); + } +#endif +} + +static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) +{ +#ifdef BE_PARANOID + int i, j; + int size; + int pi, bi; + for (i = 0; i < 8; i++) { + size = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_SIZE, i); + if (size == 0) + continue; + + hdmi_set_dip_index(codec, pin_nid, i, 0x0); + for (j = 1; j < 1000; j++) { + hdmi_write_dip_byte(codec, pin_nid, 0x0); + hdmi_get_dip_index(codec, pin_nid, &pi, &bi); + if (pi != i) + snd_printd(KERN_INFO "dip index %d: %d != %d\n", + bi, pi, i); + if (bi == 0) /* byte index wrapped around */ + break; + } + snd_printd(KERN_INFO + "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", + i, size, j); + } +#endif +} + +static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 sum = 0; + int i; + + ai->checksum = 0; + + for (i = 0; i < sizeof(*ai); i++) + sum += bytes[i]; + + ai->checksum = -sum; +} + +static void hdmi_fill_audio_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + int i; + + hdmi_debug_dip_size(codec, pin_nid); + hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ + + hdmi_checksum_audio_infoframe(ai); + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) + hdmi_write_dip_byte(codec, pin_nid, bytes[i]); +} + +static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_audio_infoframe *ai) +{ + u8 *bytes = (u8 *)ai; + u8 val; + int i; + + if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) + != AC_DIPXMIT_BEST) + return false; + + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); + for (i = 0; i < sizeof(*ai); i++) { + val = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_DIP_DATA, 0); + if (val != bytes[i]) + return false; + } + + return true; +} + +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t pin_nid; + int i; + struct hdmi_audio_infoframe ai = { + .type = 0x84, + .ver = 0x01, + .len = 0x0a, + .CC02_CT47 = substream->runtime->channels - 1, + }; + + hdmi_setup_channel_allocation(codec, nid, &ai); + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_cvt[i] != nid) + continue; + if (!spec->sink_eld[i].monitor_present) + continue; + + pin_nid = spec->pin[i]; + if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + hdmi_setup_channel_mapping(codec, pin_nid, &ai); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, &ai); + hdmi_start_infoframe_trans(codec, pin_nid); + } + } +} + + +/* + * Unsolicited events + */ + +static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + struct hdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int pind = !!(res & AC_UNSOL_RES_PD); + int eldv = !!(res & AC_UNSOL_RES_ELDV); + int index; + + printk(KERN_INFO + "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + tag, pind, eldv); + + index = hda_node_index(spec->pin, tag); + if (index < 0) + return; + + spec->sink_eld[index].monitor_present = pind; + spec->sink_eld[index].eld_valid = eldv; + + if (pind && eldv) { + hdmi_get_show_eld(codec, spec->pin[index], + &spec->sink_eld[index]); + /* TODO: do real things about ELD */ + } +} + +static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); + int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); + + printk(KERN_INFO + "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", + tag, + subtag, + cp_state, + cp_ready); + + /* TODO */ + if (cp_state) + ; + if (cp_ready) + ; +} + + +static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct hdmi_spec *spec = codec->spec; + int tag = res >> AC_UNSOL_RES_TAG_SHIFT; + int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; + + if (hda_node_index(spec->pin, tag) < 0) { + snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); + return; + } + + if (subtag == 0) + hdmi_intrinsic_event(codec, res); + else + hdmi_non_intrinsic_event(codec, res); +} + +/* + * Callbacks + */ + +static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, int format) +{ + int tag; + int fmt; + + tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; + fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); + + snd_printdd("hdmi_setup_stream: " + "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", + nid, + tag == stream_tag ? "" : "new-", + stream_tag, + fmt == format ? "" : "new-", + format); + + if (tag != stream_tag) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, + stream_tag << 4); + if (fmt != format) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_STREAM_FORMAT, format); +} + +/* + * HDA/HDMI auto parsing + */ + +static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec = codec->spec; + hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + int conn_len, curr; + int index; + + if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { + snd_printk(KERN_WARNING + "HDMI: pin %d wcaps %#x " + "does not support connection list\n", + pin_nid, get_wcaps(codec, pin_nid)); + return -EINVAL; + } + + conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, + HDA_MAX_CONNECTIONS); + if (conn_len > 1) + curr = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + else + curr = 0; + + index = hda_node_index(spec->pin, pin_nid); + if (index < 0) + return -EINVAL; + + spec->pin_cvt[index] = conn_list[curr]; + + return 0; +} + +static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, + struct hdmi_eld *eld) +{ + int present = snd_hda_pin_sense(codec, pin_nid); + + eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); + eld->eld_valid = !!(present & AC_PINSENSE_ELDV); + + if (present & AC_PINSENSE_ELDV) + hdmi_get_show_eld(codec, pin_nid, eld); +} + +static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->num_pins >= MAX_HDMI_PINS) { + snd_printk(KERN_WARNING + "HDMI: no space for pin %d\n", pin_nid); + return -EINVAL; + } + + hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); + + spec->pin[spec->num_pins] = pin_nid; + spec->num_pins++; + + /* + * It is assumed that converter nodes come first in the node list and + * hence have been registered and usable now. + */ + return hdmi_read_pin_conn(codec, pin_nid); +} + +static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->num_cvts >= MAX_HDMI_CVTS) { + snd_printk(KERN_WARNING + "HDMI: no space for converter %d\n", nid); + return -EINVAL; + } + + spec->cvt[spec->num_cvts] = nid; + spec->num_cvts++; + + return 0; +} + +static int hdmi_parse_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + int i, nodes; + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + if (!nid || nodes < 0) { + snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + case AC_WID_AUD_OUT: + if (hdmi_add_cvt(codec, nid) < 0) + return -EINVAL; + break; + case AC_WID_PIN: + caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) + continue; + if (hdmi_add_pin(codec, nid) < 0) + return -EINVAL; + break; + } + } + + /* + * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event + * can be lost and presence sense verb will become inaccurate if the + * HDA link is powered off at hot plug or hw initialization time. + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & + AC_PWRST_EPSS)) + codec->bus->power_keep_link_on = 1; +#endif + + return 0; +} + diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 918f40378d52..88d035104cc5 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -40,815 +40,20 @@ * * The HDA correspondence of pipes/ports are converter/pin nodes. */ -#define INTEL_HDMI_CVTS 2 -#define INTEL_HDMI_PINS 3 +#define MAX_HDMI_CVTS 2 +#define MAX_HDMI_PINS 3 -static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = { +#include "patch_hdmi.c" + +static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = { "INTEL HDMI 0", "INTEL HDMI 1", }; -struct intel_hdmi_spec { - int num_cvts; - int num_pins; - hda_nid_t cvt[INTEL_HDMI_CVTS+1]; /* audio sources */ - hda_nid_t pin[INTEL_HDMI_PINS+1]; /* audio sinks */ - - /* - * source connection for each pin - */ - hda_nid_t pin_cvt[INTEL_HDMI_PINS+1]; - - /* - * HDMI sink attached to each pin - */ - struct hdmi_eld sink_eld[INTEL_HDMI_PINS]; - - /* - * export one pcm per pipe - */ - struct hda_pcm pcm_rec[INTEL_HDMI_CVTS]; -}; - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; /* PB0 */ - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; - u8 reserved[5]; /* PB6 - PB10 */ -}; - -/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_setup_channel_allocation(). - */ -static struct cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - -/* - * HDA/HDMI auto parsing - */ - -static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) -{ - int i; - - for (i = 0; nids[i]; i++) - if (nids[i] == nid) - return i; - - snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); - return -EINVAL; -} - -static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct intel_hdmi_spec *spec = codec->spec; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - int conn_len, curr; - int index; - - if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { - snd_printk(KERN_WARNING - "HDMI: pin %d wcaps %#x " - "does not support connection list\n", - pin_nid, get_wcaps(codec, pin_nid)); - return -EINVAL; - } - - conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, - HDA_MAX_CONNECTIONS); - if (conn_len > 1) - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - else - curr = 0; - - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) - return -EINVAL; - - spec->pin_cvt[index] = conn_list[curr]; - - return 0; -} - -static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_eld *eld) -{ - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) - snd_hdmi_show_eld(eld); -} - -static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_eld *eld) -{ - int present = snd_hda_pin_sense(codec, pin_nid); - - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - eld->eld_valid = !!(present & AC_PINSENSE_ELDV); - - if (present & AC_PINSENSE_ELDV) - hdmi_get_show_eld(codec, pin_nid, eld); -} - -static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct intel_hdmi_spec *spec = codec->spec; - - if (spec->num_pins >= INTEL_HDMI_PINS) { - snd_printk(KERN_WARNING - "HDMI: no space for pin %d \n", pin_nid); - return -EINVAL; - } - - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); - - spec->pin[spec->num_pins] = pin_nid; - spec->num_pins++; - - /* - * It is assumed that converter nodes come first in the node list and - * hence have been registered and usable now. - */ - return intel_hdmi_read_pin_conn(codec, pin_nid); -} - -static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) -{ - struct intel_hdmi_spec *spec = codec->spec; - - if (spec->num_cvts >= INTEL_HDMI_CVTS) { - snd_printk(KERN_WARNING - "HDMI: no space for converter %d \n", nid); - return -EINVAL; - } - - spec->cvt[spec->num_cvts] = nid; - spec->num_cvts++; - - return 0; -} - -static int intel_hdmi_parse_codec(struct hda_codec *codec) -{ - hda_nid_t nid; - int i, nodes; - - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (!nid || nodes < 0) { - snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); - return -EINVAL; - } - - for (i = 0; i < nodes; i++, nid++) { - unsigned int caps; - unsigned int type; - - caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); - type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - switch (type) { - case AC_WID_AUD_OUT: - if (intel_hdmi_add_cvt(codec, nid) < 0) - return -EINVAL; - break; - case AC_WID_PIN: - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - continue; - if (intel_hdmi_add_pin(codec, nid) < 0) - return -EINVAL; - break; - } - } - - /* - * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event - * can be lost and presence sense verb will become inaccurate if the - * HDA link is powered off at hot plug or hw initialization time. - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & - AC_PWRST_EPSS)) - codec->bus->power_keep_link_on = 1; -#endif - - return 0; -} - -/* - * HDMI routines - */ - -#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int *packet_index, int *byte_index) -{ - int val; - - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_INDEX, 0); - - *packet_index = val >> 5; - *byte_index = val & 0x1f; -} -#endif - -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int packet_index, int byte_index) -{ - int val; - - val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); -} - -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, - unsigned char val) -{ - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); -} - -static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) -{ - /* Unmute */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Enable pin out */ - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); -} - -/* - * Enable Audio InfoFrame Transmission - */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_BEST); -} - -/* - * Disable Audio InfoFrame Transmission - */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_DISABLE); -} - -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) -{ - return 1 + snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, nid)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - -static void hdmi_debug_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int slot; - - for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, i); - printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0xf); - } -#endif -} - - -/* - * Audio InfoFrame routines - */ - -static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int size; - - size = snd_hdmi_get_eld_size(codec, pin_nid); - printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); - - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); - } -#endif -} - -static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef BE_PARANOID - int i, j; - int size; - int pi, bi; - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - if (size == 0) - continue; - - hdmi_set_dip_index(codec, pin_nid, i, 0x0); - for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, pin_nid, 0x0); - hdmi_get_dip_index(codec, pin_nid, &pi, &bi); - if (pi != i) - snd_printd(KERN_INFO "dip index %d: %d != %d\n", - bi, pi, i); - if (bi == 0) /* byte index wrapped around */ - break; - } - snd_printd(KERN_INFO - "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", - i, size, j); - } -#endif -} - -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) -{ - u8 *bytes = (u8 *)ai; - u8 sum = 0; - int i; - - ai->checksum = 0; - - for (i = 0; i < sizeof(*ai); i++) - sum += bytes[i]; - - ai->checksum = - sum; -} - -static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - u8 *bytes = (u8 *)ai; - int i; - - hdmi_debug_dip_size(codec, pin_nid); - hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - - hdmi_checksum_audio_infoframe(ai); - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) - hdmi_write_dip_byte(codec, pin_nid, bytes[i]); -} - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, - struct hdmi_audio_infoframe *ai) -{ - struct intel_hdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; - int i; - int spk_mask = 0; - int channels = 1 + (ai->CC02_CT47 & 0x7); - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - i = hda_node_index(spec->pin_cvt, nid); - if (i < 0) - return 0; - eld = &spec->sink_eld[i]; - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!eld->spk_alloc) - eld->spk_alloc = 0xffff; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ai->CA = channel_allocations[i].ca_index; - break; - } - } - - snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); - snd_printdd(KERN_INFO - "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ai->CA, channels, buf); - - return ai->CA; -} - -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - int i; - int ca = ai->CA; - int err; - - if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[ca].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (i << 4); - } - - for (i = 0; i < 8; i++) { - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - hdmi_channel_mapping[ca][i]); - if (err) { - snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); - break; - } - } - - hdmi_debug_channel_mapping(codec, pin_nid); -} - -static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - u8 *bytes = (u8 *)ai; - u8 val; - int i; - - if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) - != AC_DIPXMIT_BEST) - return false; - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) { - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != bytes[i]) - return false; - } - - return true; -} - -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, - struct snd_pcm_substream *substream) -{ - struct intel_hdmi_spec *spec = codec->spec; - hda_nid_t pin_nid; - int i; - struct hdmi_audio_infoframe ai = { - .type = 0x84, - .ver = 0x01, - .len = 0x0a, - .CC02_CT47 = substream->runtime->channels - 1, - }; - - hdmi_setup_channel_allocation(codec, nid, &ai); - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!spec->sink_eld[i].monitor_present) - continue; - - pin_nid = spec->pin[i]; - if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { - hdmi_setup_channel_mapping(codec, pin_nid, &ai); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); - } - } -} - - /* - * Unsolicited events + * HDMI callbacks */ -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - struct intel_hdmi_spec *spec = codec->spec; - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int pind = !!(res & AC_UNSOL_RES_PD); - int eldv = !!(res & AC_UNSOL_RES_ELDV); - int index; - - printk(KERN_INFO - "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - tag, pind, eldv); - - index = hda_node_index(spec->pin, tag); - if (index < 0) - return; - - spec->sink_eld[index].monitor_present = pind; - spec->sink_eld[index].eld_valid = eldv; - - if (pind && eldv) { - hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]); - /* TODO: do real things about ELD */ - } -} - -static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); - int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); - - printk(KERN_INFO - "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", - tag, - subtag, - cp_state, - cp_ready); - - /* TODO */ - if (cp_state) - ; - if (cp_ready) - ; -} - - -static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct intel_hdmi_spec *spec = codec->spec; - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - - if (hda_node_index(spec->pin, tag) < 0) { - snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); - return; - } - - if (subtag == 0) - hdmi_intrinsic_event(codec, res); - else - hdmi_non_intrinsic_event(codec, res); -} - -/* - * Callbacks - */ - -static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, int format) -{ - int tag; - int fmt; - - tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; - fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); - - snd_printdd("hdmi_setup_stream: " - "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", - nid, - tag == stream_tag ? "" : "new-", - stream_tag, - fmt == format ? "" : "new-", - format); - - if (tag != stream_tag) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4); - if (fmt != format) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, format); -} - static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, @@ -882,7 +87,7 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = { static int intel_hdmi_build_pcms(struct hda_codec *codec) { - struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; int i; @@ -908,7 +113,7 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec) static int intel_hdmi_build_controls(struct hda_codec *codec) { - struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int err; int i; @@ -923,7 +128,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec) static int intel_hdmi_init(struct hda_codec *codec) { - struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int i; for (i = 0; spec->pin[i]; i++) { @@ -937,7 +142,7 @@ static int intel_hdmi_init(struct hda_codec *codec) static void intel_hdmi_free(struct hda_codec *codec) { - struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int i; for (i = 0; i < spec->num_pins; i++) @@ -951,12 +156,12 @@ static struct hda_codec_ops intel_hdmi_patch_ops = { .free = intel_hdmi_free, .build_pcms = intel_hdmi_build_pcms, .build_controls = intel_hdmi_build_controls, - .unsol_event = intel_hdmi_unsol_event, + .unsol_event = hdmi_unsol_event, }; static int patch_intel_hdmi(struct hda_codec *codec) { - struct intel_hdmi_spec *spec; + struct hdmi_spec *spec; int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -964,7 +169,7 @@ static int patch_intel_hdmi(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; - if (intel_hdmi_parse_codec(codec) < 0) { + if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; kfree(spec); return -EINVAL; diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 1c774f942407..70669a246902 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -29,6 +29,15 @@ #include "hda_codec.h" #include "hda_local.h" +#define MAX_HDMI_CVTS 1 +#define MAX_HDMI_PINS 1 + +#include "patch_hdmi.c" + +static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = { + "NVIDIA HDMI", +}; + /* define below to restrict the supported rates and formats */ /* #define LIMITED_RATE_FMT_SUPPORT */ @@ -83,802 +92,12 @@ static struct hda_verb nvhdmi_basic_init_7x[] = { (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) #endif -#define NVIDIA_89_HDMI_CVTS 1 -#define NVIDIA_89_HDMI_PINS 1 - -static char *nvhdmi_pcm_names[NVIDIA_89_HDMI_CVTS] = { - "NVIDIA HDMI", -}; - -struct nvhdmi_spec { - int num_cvts; - int num_pins; - hda_nid_t cvt[NVIDIA_89_HDMI_CVTS+1]; /* audio sources */ - hda_nid_t pin[NVIDIA_89_HDMI_PINS+1]; /* audio sinks */ - hda_nid_t pin_cvt[NVIDIA_89_HDMI_PINS+1]; - struct hda_pcm pcm_rec[NVIDIA_89_HDMI_CVTS]; - struct hdmi_eld sink_eld[NVIDIA_89_HDMI_PINS]; - struct hda_multi_out multiout; - unsigned int codec_type; -}; - -struct hdmi_audio_infoframe { - u8 type; /* 0x84 */ - u8 ver; /* 0x01 */ - u8 len; /* 0x0a */ - - u8 checksum; /* PB0 */ - u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */ - u8 SS01_SF24; - u8 CXT04; - u8 CA; - u8 LFEPBL01_LSV36_DM_INH7; -}; - -/* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x32, 0x23, 0x64, 0x75, 0x46, 0x57 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_setup_channel_allocation(). - */ -static struct cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - -/* - * HDA/HDMI auto parsing - */ - -static int hda_node_index(hda_nid_t *nids, hda_nid_t nid) -{ - int i; - - for (i = 0; nids[i]; i++) - if (nids[i] == nid) - return i; - - snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid); - return -EINVAL; -} - -static int nvhdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct nvhdmi_spec *spec = codec->spec; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; - int conn_len, curr; - int index; - - if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { - snd_printk(KERN_WARNING - "HDMI: pin %d wcaps %#x " - "does not support connection list\n", - pin_nid, get_wcaps(codec, pin_nid)); - return -EINVAL; - } - - conn_len = snd_hda_get_connections(codec, pin_nid, conn_list, - HDA_MAX_CONNECTIONS); - if (conn_len > 1) - curr = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - else - curr = 0; - - index = hda_node_index(spec->pin, pin_nid); - if (index < 0) - return -EINVAL; - - spec->pin_cvt[index] = conn_list[curr]; - - return 0; -} - -static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_eld *eld) -{ - if (!snd_hdmi_get_eld(eld, codec, pin_nid)) - snd_hdmi_show_eld(eld); -} - -static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_eld *eld) -{ - int present = snd_hda_pin_sense(codec, pin_nid); - - eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); - eld->eld_valid = !!(present & AC_PINSENSE_ELDV); - - if (present & AC_PINSENSE_ELDV) - hdmi_get_show_eld(codec, pin_nid, eld); -} - -static int nvhdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) -{ - struct nvhdmi_spec *spec = codec->spec; - - if (spec->num_pins >= NVIDIA_89_HDMI_PINS) { - snd_printk(KERN_WARNING - "HDMI: no space for pin %d \n", pin_nid); - return -EINVAL; - } - - hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); - - spec->pin[spec->num_pins] = pin_nid; - spec->num_pins++; - - /* - * It is assumed that converter nodes come first in the node list and - * hence have been registered and usable now. - */ - return nvhdmi_read_pin_conn(codec, pin_nid); -} - -static int nvhdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) -{ - struct nvhdmi_spec *spec = codec->spec; - - if (spec->num_cvts >= NVIDIA_89_HDMI_CVTS) { - snd_printk(KERN_WARNING - "HDMI: no space for converter %d \n", nid); - return -EINVAL; - } - - spec->cvt[spec->num_cvts] = nid; - spec->num_cvts++; - - return 0; -} - - -static int nvhdmi_parse_codec(struct hda_codec *codec) -{ - hda_nid_t nid; - int i, nodes; - - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (!nid || nodes < 0) { - snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n"); - return -EINVAL; - } - - for (i = 0; i < nodes; i++, nid++) { - unsigned int caps; - unsigned int type; - - caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); - type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_DIGITAL)) - continue; - - switch (type) { - case AC_WID_AUD_OUT: - if (nvhdmi_add_cvt(codec, nid) < 0) - return -EINVAL; - break; - case AC_WID_PIN: - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); - if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) - continue; - if (nvhdmi_add_pin(codec, nid) < 0) - return -EINVAL; - break; - } - } - - /* - * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event - * can be lost and presence sense verb will become inaccurate if the - * HDA link is powered off at hot plug or hw initialization time. - */ -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) & - AC_PWRST_EPSS)) - codec->bus->power_keep_link_on = 1; -#endif - - return 0; -} - -/* - * HDMI routines - */ - -#ifdef BE_PARANOID -static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int *packet_index, int *byte_index) -{ - int val; - - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_INDEX, 0); - - *packet_index = val >> 5; - *byte_index = val & 0x1f; -} -#endif - -static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid, - int packet_index, int byte_index) -{ - int val; - - val = (packet_index << 5) | (byte_index & 0x1f); - - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); -} - -static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid, - unsigned char val) -{ - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); -} - -static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid) -{ - /* Unmute */ - if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); - /* Enable pin out */ - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); -} - -/* - * Enable Audio InfoFrame Transmission - */ -static void hdmi_start_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_BEST); -} - -/* - * Disable Audio InfoFrame Transmission - */ -static void hdmi_stop_infoframe_trans(struct hda_codec *codec, - hda_nid_t pin_nid) -{ - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT, - AC_DIPXMIT_DISABLE); -} - -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid) -{ - return 1 + snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, nid)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - -static void hdmi_debug_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int slot; - - for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, i); - printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0xf); - } -#endif -} - - -/* - * Audio InfoFrame routines - */ - -static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - int i; - int size; - - size = snd_hdmi_get_eld_size(codec, pin_nid); - printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size); - - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size); - } -#endif -} - -static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid) -{ -#ifdef BE_PARANOID - int i, j; - int size; - int pi, bi; - for (i = 0; i < 8; i++) { - size = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_SIZE, i); - if (size == 0) - continue; - - hdmi_set_dip_index(codec, pin_nid, i, 0x0); - for (j = 1; j < 1000; j++) { - hdmi_write_dip_byte(codec, pin_nid, 0x0); - hdmi_get_dip_index(codec, pin_nid, &pi, &bi); - if (pi != i) - snd_printd(KERN_INFO "dip index %d: %d != %d\n", - bi, pi, i); - if (bi == 0) /* byte index wrapped around */ - break; - } - snd_printd(KERN_INFO - "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n", - i, size, j); - } -#endif -} - -static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai) -{ - ai->checksum = 0; -} - -static void hdmi_fill_audio_infoframe(struct hda_codec *codec, - hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - u8 *bytes = (u8 *)ai; - int i; - - hdmi_debug_dip_size(codec, pin_nid); - hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */ - - hdmi_checksum_audio_infoframe(ai); - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) - hdmi_write_dip_byte(codec, pin_nid, bytes[i]); -} - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, - struct hdmi_audio_infoframe *ai) -{ - struct nvhdmi_spec *spec = codec->spec; - struct hdmi_eld *eld; - int i; - int spk_mask = 0; - int channels = 1 + (ai->CC02_CT47 & 0x7); - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - i = hda_node_index(spec->pin_cvt, nid); - if (i < 0) - return 0; - eld = &spec->sink_eld[i]; - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!eld->spk_alloc) - eld->spk_alloc = 0xffff; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ai->CA = channel_allocations[i].ca_index; - break; - } - } - - snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); - snd_printdd(KERN_INFO - "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ai->CA, channels, buf); - - return ai->CA; -} - -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - int i; - int ca = ai->CA; - int err; - - if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[ca].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (i << 4); - } - - for (i = 0; i < 8; i++) { - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - hdmi_channel_mapping[ca][i]); - if (err) { - snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); - break; - } - } - - hdmi_debug_channel_mapping(codec, pin_nid); -} - -static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, - struct hdmi_audio_infoframe *ai) -{ - u8 *bytes = (u8 *)ai; - u8 val; - int i; - - if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) - != AC_DIPXMIT_BEST) - return false; - - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(*ai); i++) { - val = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_DIP_DATA, 0); - if (val != bytes[i]) - return false; - } - - return true; -} - -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, - struct snd_pcm_substream *substream) -{ - struct nvhdmi_spec *spec = codec->spec; - hda_nid_t pin_nid; - int i; - struct hdmi_audio_infoframe ai = { - .type = 0x84, - .ver = 0x01, - .len = 0x0a, - .CC02_CT47 = substream->runtime->channels - 1, - }; - - hdmi_setup_channel_allocation(codec, nid, &ai); - - for (i = 0; i < spec->num_pins; i++) { - if (spec->pin_cvt[i] != nid) - continue; - if (!spec->sink_eld[i].monitor_present) - continue; - - pin_nid = spec->pin[i]; - if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { - hdmi_setup_channel_mapping(codec, pin_nid, &ai); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, &ai); - hdmi_start_infoframe_trans(codec, pin_nid); - } - } -} - -/* - * Unsolicited events - */ - -static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - struct nvhdmi_spec *spec = codec->spec; - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int pind = !!(res & AC_UNSOL_RES_PD); - int eldv = !!(res & AC_UNSOL_RES_ELDV); - int index; - - printk(KERN_INFO - "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - tag, pind, eldv); - - index = hda_node_index(spec->pin, tag); - if (index < 0) - return; - - spec->sink_eld[index].monitor_present = pind; - spec->sink_eld[index].eld_valid = eldv; - - if (eldv) { - spec->sink_eld[index].monitor_present = 1; - hdmi_get_show_eld(codec, spec->pin[index], - &spec->sink_eld[index]); - /* TODO: do real things about ELD */ - } -} - -static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) -{ - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - int cp_state = !!(res & AC_UNSOL_RES_CP_STATE); - int cp_ready = !!(res & AC_UNSOL_RES_CP_READY); - - printk(KERN_INFO - "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n", - tag, - subtag, - cp_state, - cp_ready); - - /* TODO */ - if (cp_state) - ; - if (cp_ready) - ; -} - -static void nvhdmi_unsol_event(struct hda_codec *codec, unsigned int res) -{ - struct nvhdmi_spec *spec = codec->spec; - int tag = res >> AC_UNSOL_RES_TAG_SHIFT; - int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT; - - if (hda_node_index(spec->pin, tag) < 0) { - snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag); - return; - } - - if (subtag == 0) - hdmi_intrinsic_event(codec, res); - else - hdmi_non_intrinsic_event(codec, res); -} - -/* - * Callbacks - */ - -static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid, - u32 stream_tag, int format) -{ - int tag; - int fmt; - - tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4; - fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0); - - snd_printdd("hdmi_setup_stream: " - "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n", - nid, - tag == stream_tag ? "" : "new-", - stream_tag, - fmt == format ? "" : "new-", - format); - - if (tag != stream_tag) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - stream_tag << 4); - if (fmt != format) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_STREAM_FORMAT, format); -} - /* * Controls */ static int nvhdmi_build_controls(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int err; int i; @@ -902,7 +121,7 @@ static int nvhdmi_build_controls(struct hda_codec *codec) static int nvhdmi_init(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int i; if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) { @@ -920,7 +139,7 @@ static int nvhdmi_init(struct hda_codec *codec) static void nvhdmi_free(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int i; if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89) @@ -939,7 +158,7 @@ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_open(codec, &spec->multiout); } @@ -947,7 +166,7 @@ static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; int i; snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, @@ -968,7 +187,7 @@ static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -1121,7 +340,7 @@ static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo, unsigned int format, struct snd_pcm_substream *substream) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, format, substream); } @@ -1170,7 +389,7 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = { static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; int i; @@ -1196,7 +415,7 @@ static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec) static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; @@ -1212,7 +431,7 @@ static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec) static int nvhdmi_build_pcms_2ch(struct hda_codec *codec) { - struct nvhdmi_spec *spec = codec->spec; + struct hdmi_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; codec->num_pcms = 1; @@ -1231,7 +450,7 @@ static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = { .build_pcms = nvhdmi_build_pcms_8ch_89, .init = nvhdmi_init, .free = nvhdmi_free, - .unsol_event = nvhdmi_unsol_event, + .unsol_event = hdmi_unsol_event, }; static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = { @@ -1250,7 +469,7 @@ static struct hda_codec_ops nvhdmi_patch_ops_2ch = { static int patch_nvhdmi_8ch_89(struct hda_codec *codec) { - struct nvhdmi_spec *spec; + struct hdmi_spec *spec; int i; spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -1260,7 +479,7 @@ static int patch_nvhdmi_8ch_89(struct hda_codec *codec) codec->spec = spec; spec->codec_type = HDA_CODEC_NVIDIA_MCP89; - if (nvhdmi_parse_codec(codec) < 0) { + if (hdmi_parse_codec(codec) < 0) { codec->spec = NULL; kfree(spec); return -EINVAL; @@ -1277,7 +496,7 @@ static int patch_nvhdmi_8ch_89(struct hda_codec *codec) static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) { - struct nvhdmi_spec *spec; + struct hdmi_spec *spec; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -1297,7 +516,7 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) static int patch_nvhdmi_2ch(struct hda_codec *codec) { - struct nvhdmi_spec *spec; + struct hdmi_spec *spec; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) -- cgit v1.2.2 From 2abbf4391fb56dfa97221ed6796782537d15196f Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Mon, 8 Mar 2010 10:45:38 +0800 Subject: ALSA: hdmi - show debug message on changing audio infoframe Also change printk level for the two others. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b2ab39670dda..2c2bafbf0258 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -398,9 +398,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid, } snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf)); - snd_printdd(KERN_INFO - "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ai->CA, channels, buf); + snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ai->CA, channels, buf); return ai->CA; } @@ -442,7 +441,8 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, AC_VERB_SET_HDMI_CHAN_SLOT, hdmi_channel_mapping[ca][i]); if (err) { - snd_printdd(KERN_INFO "HDMI: channel mapping failed\n"); + snd_printdd(KERN_NOTICE + "HDMI: channel mapping failed\n"); break; } } @@ -599,6 +599,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, pin_nid = spec->pin[i]; if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) { + snd_printdd("hdmi_setup_audio_infoframe: " + "cvt=%d pin=%d channels=%d\n", + nid, pin_nid, + substream->runtime->channels); hdmi_setup_channel_mapping(codec, pin_nid, &ai); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, &ai); -- cgit v1.2.2 From 50ae0aa8f55813b2cc5e5b7f589f328b8fcd45ec Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Mar 2010 12:09:59 +0100 Subject: ALSA: hda - Fix wrong model range check for ALC268 Fix a wrong value passed to snd_hda_check_board_codec_sid_config() as the upper-limit in parse_alc268(), so that any wrong value can't be passed. So far, no bogus value was set in the quirk entries, so this won't give any behavioral changes. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5d2fbb87b871..dcd8a2cadd99 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13201,7 +13201,7 @@ static int patch_alc268(struct hda_codec *codec) if (board_config < 0 || board_config >= ALC268_MODEL_LAST) board_config = snd_hda_check_board_codec_sid_config(codec, - ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); + ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl); if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", -- cgit v1.2.2 From 5311114d4867113c00f78829d4ce14be458ec925 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 8 Mar 2010 12:13:07 +0100 Subject: ALSA: hda - Fix input source elements of secondary ADCs on Realtek Since alc_auto_create_input_ctls() doesn't set the elements for the secondary ADCs, "Input Source" elemtns for these also get empty, resulting in buggy outputs of alsactl like: control.14 { comment.access 'read write' comment.type ENUMERATED comment.count 1 iface MIXER name 'Input Source' index 1 value 0 } This patch fixes alc_mux_enum_*() (and others) to fall back to the first entry if the secondary input mux is empty. Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dcd8a2cadd99..3a8371990d75 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -411,6 +411,8 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id); if (mux_idx >= spec->num_mux_defs) mux_idx = 0; + if (!spec->input_mux[mux_idx].num_items && mux_idx > 0) + mux_idx = 0; return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); } @@ -439,6 +441,8 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; + if (!imux->num_items && mux_idx > 0) + imux = &spec->input_mux[0]; type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_AUD_MIX) { @@ -10105,6 +10109,8 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) continue; mux_idx = c >= spec->num_mux_defs ? 0 : c; imux = &spec->input_mux[mux_idx]; + if (!imux->num_items && mux_idx > 0) + imux = &spec->input_mux[0]; for (idx = 0; idx < conns; idx++) { /* if the current connection is the selected one, * unmute it as default - otherwise mute it -- cgit v1.2.2 From ecd216260f87dd8c14b2580a16f055554644bbea Mon Sep 17 00:00:00 2001 From: Ralf Gerbig Date: Tue, 9 Mar 2010 18:25:47 +0100 Subject: ALSA: hda - Sound MSI fallout on a Asus mobo NVIDIA MCP55 without the following patch audio ssttuutteerrs on ASUS M2N32-SLI PREMIUM ACPI BIOS Revision 1304 the sound device is: 00:0e.1 Audio device: nVidia Corporation MCP55 High Definition Audio (rev a2) worked with 2.6.32 Signed-off-by: Ralf Gerbig Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e37bffec749a..10bbb534d3ca 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2358,6 +2358,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev) static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ + SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ {} }; -- cgit v1.2.2 From c602c8ad45d6ee6ad91fc544513cc96f70790983 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Mon, 15 Mar 2010 09:01:26 +0100 Subject: ALSA: hda - New Intel HDA controller Added a PCI controller id on new Dell laptops. Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 10bbb534d3ca..926815201885 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2706,6 +2706,7 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH }, /* PCH */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH }, + { PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH }, /* CPT */ { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH }, /* SCH */ -- cgit v1.2.2 From 28d1a85e136985982448b2f9b1342bae85ad1c98 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 15 Mar 2010 09:05:46 +0100 Subject: ALSA: hda - Add an error message for invalid mapping NID Add an error message to snd_hda_add_nid() for invalid mapping NID to make easier to hunt the buggy code. Also added a missing space to the error message in snd_hda_build_controls() Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5bd7cf45f3a5..0e76ac2b2ace 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1806,6 +1806,8 @@ int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl, item->nid = nid; return 0; } + printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n", + kctl->id.name, kctl->id.index, index); return -EINVAL; } EXPORT_SYMBOL_HDA(snd_hda_add_nid); @@ -2884,7 +2886,7 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { int err = snd_hda_codec_build_controls(codec); if (err < 0) { - printk(KERN_ERR "hda_codec: cannot build controls" + printk(KERN_ERR "hda_codec: cannot build controls " "for #%d (error %d)\n", codec->addr, err); err = snd_hda_codec_reset(codec); if (err < 0) { -- cgit v1.2.2 From 9c4cc0bdede1c39bde60a0d5d9251aac71fbe719 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 15 Mar 2010 09:07:52 +0100 Subject: ALSA: hda - Fix secondary ADC of ALC260 basic model Fix adc_nids[] for ALC260 basic model to match with num_adc_nids. Otherwise you get an invalid NID in the secondary "Input Source" mixer element. Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3a8371990d75..ba45868d5242 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6477,7 +6477,7 @@ static struct alc_config_preset alc260_presets[] = { .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), - .adc_nids = alc260_adc_nids, + .adc_nids = alc260_dual_adc_nids, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, -- cgit v1.2.2 From b43f6e5e258d67acae5961896d10bbe38c271070 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Wed, 10 Mar 2010 19:17:46 +0100 Subject: ALSA: hda - Add PCI quirks for MSI NetOn AP1900 and Wind Top AE2220 This should make the speakers and jack detection work on MSI all-in-one computers NetOn AP1900 and Wind Top AE2220. Signed-off-by: Anisse Astier Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ba45868d5242..07637c4aa46f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9195,6 +9195,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG), SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), @@ -9204,6 +9205,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), -- cgit v1.2.2 From 80c43ed724797627d8f86855248c497a6161a214 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 15 Mar 2010 15:51:53 +0100 Subject: ALSA: hda - Disable MSI for Nvidia controller Judging from the member of enable_msi white-list, Nvidia controller seems to cause troubles with MSI enabled, e.g. boot hang up or other serious issue may come up. It's safer to disable MSI as default for Nvidia controllers again for now. Signed-off-by: Takashi Iwai Cc: --- sound/pci/hda/hda_intel.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 926815201885..027d3f4c1c59 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2378,6 +2378,13 @@ static void __devinit check_msi(struct azx *chip) "hda_intel: msi for device %04x:%04x set to %d\n", q->subvendor, q->subdevice, q->value); chip->msi = q->value; + return; + } + + /* NVidia chipsets seem to cause troubles with MSI */ + if (chip->driver_type == AZX_DRIVER_NVIDIA) { + printk(KERN_INFO "hda_intel: Disable MSI for Nvidia chipset\n"); + chip->msi = 0; } } -- cgit v1.2.2 From 572c0e3c73341755f3e7dfaaef6b26df12bd709c Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 14 Mar 2010 23:44:03 -0400 Subject: ALSA: hda: Use LPIB and 6stack-dig for eMachines T5212 BugLink: https://bugs.launchpad.net/bugs/538895 The OR has verified that both position_fix=1 and model=6stack-dig are necessary to have capture function properly. (The existing 3stack-6ch model quirk seems to be incorrect.) Reported-by: Reuben Bailey Tested-by: Reuben Bailey Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + sound/pci/hda/patch_realtek.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 027d3f4c1c59..1766ad2926d6 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2271,6 +2271,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), + SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), {} }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 07637c4aa46f..4ec57633af88 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9237,7 +9237,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL), SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL), SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL), - SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG), {} }; -- cgit v1.2.2 From e3d2530a6cea80987f77b75d8784a00f3aaf22ff Mon Sep 17 00:00:00 2001 From: Kunal Gangakhedkar Date: Sat, 20 Mar 2010 23:08:01 +0530 Subject: ALSA: hda - Add PCI quirk for HP dv6-1110ax. Adding this PCI quirk fixes the board config detection. This also fixes jack sensing by using "hp_detect=1" via properly detected board config. Signed-off-by: Kunal Gangakhedkar Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8c416bb18a57..c4be3fab94e5 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1730,6 +1730,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "HP HDX", STAC_HP_HDX), /* HDX16 */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620, "HP dv6", STAC_HP_DV5), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061, + "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, "HP", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, -- cgit v1.2.2 From 025f206c9e0f96cc41567b01c07fb852d8900da1 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 21 Mar 2010 18:34:43 -0400 Subject: ALSA: hda: Fix 0 dB offset for HP laptops using CX20551 (Waikiki) BugLink: https://launchpad.net/bugs/420578 The OR has verified that his hardware distorts because of the 0 dB offset not corresponding to the highest PCM level. Fix this by capping said PCM level to 0 dB similarly to what we do for CX20549 (Venice). Reported-by: Mike Pontillo Tested-by: Mike Pontillo Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 194a28c54992..61682e1d09da 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1591,6 +1591,21 @@ static int patch_cxt5047(struct hda_codec *codec) #endif } spec->vmaster_nid = 0x13; + + switch (codec->subsystem_id >> 16) { + case 0x103c: + /* HP laptops have really bad sound over 0 dB on NID 0x10. + * Fix max PCM level to 0 dB (originally it has 0x1e steps + * with 0 dB offset 0x17) + */ + snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + break; + } + return 0; } -- cgit v1.2.2 From e933e9e5238b79870b04718024416a6dcf602a27 Mon Sep 17 00:00:00 2001 From: Derek Kelly Date: Mon, 22 Mar 2010 08:04:19 +0100 Subject: ALSA: hda - Add support of Nvidia GT220 HDMI This patch adds the device id for Nvidia GT220 cards to the nvhdmi driver. I have tested it and confirmed it to be working. Original patch download link: https://gist.github.com/324070/ Signed-off-by: Derek Kelly Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_nvhdmi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 70669a246902..9e47717c8e2a 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -554,6 +554,8 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000b, .name = "GT21x HDMI", .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de000a, .name = "GT220 HDMI", + .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000d, .name = "GT240 HDMI", .patch = patch_nvhdmi_8ch_89 }, {} /* terminator */ @@ -568,6 +570,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0067"); MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_ALIAS("snd-hda-codec-id:10de000c"); MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000a"); MODULE_ALIAS("snd-hda-codec-id:10de000d"); MODULE_LICENSE("GPL"); -- cgit v1.2.2 From ea823c08912cfb6d4af2fa8b6dd5d8deb2fb486a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Mar 2010 08:07:55 +0100 Subject: ALSA: hda - Sort codec entry list of Nvidia HDMI Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_nvhdmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 9e47717c8e2a..3c10c0b149f4 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -538,8 +538,6 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec) * patch entries */ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, - { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0003, .name = "MCP77/78 HDMI", @@ -550,14 +548,16 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { .patch = patch_nvhdmi_8ch_7x }, { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x }, - { .id = 0x10de000c, .name = "MCP89 HDMI", + { .id = 0x10de000a, .name = "GT220 HDMI", .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000b, .name = "GT21x HDMI", .patch = patch_nvhdmi_8ch_89 }, - { .id = 0x10de000a, .name = "GT220 HDMI", + { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 }, { .id = 0x10de000d, .name = "GT240 HDMI", .patch = patch_nvhdmi_8ch_89 }, + { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch }, + { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch }, {} /* terminator */ }; @@ -566,12 +566,12 @@ MODULE_ALIAS("snd-hda-codec-id:10de0003"); MODULE_ALIAS("snd-hda-codec-id:10de0005"); MODULE_ALIAS("snd-hda-codec-id:10de0006"); MODULE_ALIAS("snd-hda-codec-id:10de0007"); -MODULE_ALIAS("snd-hda-codec-id:10de0067"); -MODULE_ALIAS("snd-hda-codec-id:10de8001"); -MODULE_ALIAS("snd-hda-codec-id:10de000c"); -MODULE_ALIAS("snd-hda-codec-id:10de000b"); MODULE_ALIAS("snd-hda-codec-id:10de000a"); +MODULE_ALIAS("snd-hda-codec-id:10de000b"); +MODULE_ALIAS("snd-hda-codec-id:10de000c"); MODULE_ALIAS("snd-hda-codec-id:10de000d"); +MODULE_ALIAS("snd-hda-codec-id:10de0067"); +MODULE_ALIAS("snd-hda-codec-id:10de8001"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec"); -- cgit v1.2.2 From bae84e70d66fe46c12231082cf1c4848ea22f3ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Mar 2010 08:30:20 +0100 Subject: ALSA: hda - Fix access-after-free in patch_realtek.c alc_free_kctls() has to be called after all jobs done in alc_build_controls(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4ec57633af88..053d53d8c8b2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2532,8 +2532,6 @@ static int alc_build_controls(struct hda_codec *codec) return err; } - alc_free_kctls(codec); /* no longer needed */ - /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); if (!kctl) @@ -2602,6 +2600,9 @@ static int alc_build_controls(struct hda_codec *codec) } } } + + alc_free_kctls(codec); /* no longer needed */ + return 0; } -- cgit v1.2.2 From 1c583063a5c769fe2ec604752e383972c69e6d9b Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 24 Mar 2010 07:10:54 +0100 Subject: ALSA: cmipci: work around invalid PCM pointer When the CMI8738 FRAME2 register is read, the chip sometimes (probably when wrapping around) returns an invalid value that would be outside the programmed DMA buffer. This leads to an inconsistent PCM pointer that is likely to result in an underrun. To work around this, read the register multiple times until we get a valid value; the error state seems to be very short-lived. Signed-off-by: Clemens Ladisch Reported-and-tested-by: Matija Nalis Cc: Signed-off-by: Takashi Iwai --- sound/pci/cmipci.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1ded64e05643..329968edca9b 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -941,13 +941,21 @@ static snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci struct snd_pcm_substream *substream) { size_t ptr; - unsigned int reg; + unsigned int reg, rem, tries; + if (!rec->running) return 0; #if 1 // this seems better.. reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; - ptr = rec->dma_size - (snd_cmipci_read_w(cm, reg) + 1); - ptr >>= rec->shift; + for (tries = 0; tries < 3; tries++) { + rem = snd_cmipci_read_w(cm, reg); + if (rem < rec->dma_size) + goto ok; + } + printk(KERN_ERR "cmipci: invalid PCM pointer: %#x\n", rem); + return SNDRV_PCM_POS_XRUN; +ok: + ptr = (rec->dma_size - (rem + 1)) >> rec->shift; #else reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; ptr = snd_cmipci_read(cm, reg) - rec->offset; -- cgit v1.2.2 From 6a4f2ccb467e00281470cde2dee08fe5ecde62d1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 25 Mar 2010 15:00:15 +0100 Subject: ALSA: hda - Don't set invalid connection index in Realtek initialiaiton Skip initialization of connections of DAC widgets that aren't used, which resulted in invalid verb parameters. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 053d53d8c8b2..9a23444e9e7a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10043,8 +10043,11 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, alc_set_pin_output(codec, nid, pin_type); if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; - else + else { + if (spec->multiout.num_dacs >= dac_idx) + return; idx = spec->multiout.dac_nids[dac_idx] - 2; + } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } -- cgit v1.2.2 From e1f7f02b45cf33a774d56e505ce1718af9392f5e Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Thu, 25 Mar 2010 22:38:15 -0700 Subject: ALSA: ac97: Add IBM ThinkPad R40e to Headphone/Line Jack Sense blacklist BugLink: https://launchpad.net/bugs/303789 This model needs both 'Headphone Jack Sense' and 'Line Jack Sense' muted for audible audio, so just add its SSID to the blacklist and don't enumerate the controls. Signed-off-by: Daniel T Chen Cc: Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_patch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 1caf5e3c1f6a..1a59b71c5432 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1852,6 +1852,7 @@ static unsigned int ad1981_jacks_blacklist[] = { 0x10140523, /* Thinkpad R40 */ 0x10140534, /* Thinkpad X31 */ 0x10140537, /* Thinkpad T41p */ + 0x1014053e, /* Thinkpad R40e */ 0x10140554, /* Thinkpad T42p/R50p */ 0x10140567, /* Thinkpad T43p 2668-G7U */ 0x10140581, /* Thinkpad X41-2527 */ -- cgit v1.2.2 From 5cd165e7057020884e430941c24454d3df9a799d Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Sun, 28 Mar 2010 13:32:34 -0700 Subject: ALSA: ac97: Add Toshiba P500 to ac97 jack sense blacklist BugLink: https://launchpad.net/bugs/481058 The OR has verified that both 'Headphone Jack Sense' and 'Line Jack Sense' need to be muted for sound to be audible, so just add the machine's SSID to the ac97 jack sense blacklist. Reported-by: Richard Gagne Tested-by: Richard Gagne Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/ac97/ac97_patch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 1a59b71c5432..e68c98ef4041 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1859,6 +1859,7 @@ static unsigned int ad1981_jacks_blacklist[] = { 0x10280160, /* Dell Dimension 2400 */ 0x104380b0, /* Asus A7V8X-MX */ 0x11790241, /* Toshiba Satellite A-15 S127 */ + 0x1179ff10, /* Toshiba P500 */ 0x144dc01a, /* Samsung NP-X20C004/SEG */ 0 /* end */ }; -- cgit v1.2.2 From 9ec8ddad59fadd8021adfea4cb716a49b0e232e9 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Sun, 28 Mar 2010 02:34:40 -0400 Subject: ALSA: hda: Use LPIB for ga-ma770-ud3 board BugLink: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=575669 The OR states that position_fix=1 is necessary to work around glitching during volume adjustments using PulseAudio. Reported-by: Carlos Laviola Tested-by: Carlos Laviola Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8b2915631cc3..4bb90675f70f 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2269,6 +2269,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), -- cgit v1.2.2 From 5dbd5ec6e1cf2e49128025d80813a275744a7ac5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Mar 2010 09:16:24 +0200 Subject: ALSA: hda - Fix invalid bit values passed to snd_hda_codec_amp_stereo() The mask and value parameters passed to snd_hda_codec_amp_stereo() should be 8-bit values for mute and volume. Passing AMP_IN_MUTE() is wrong, which is found in many places in patch_realtek.c as a left-over from the conversion to snd_hda_codec_amp_stereo(). Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a23444e9e7a..bc55c1e96df5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12459,11 +12459,11 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, 0x15); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc268_acer_lc_unsol_event(struct hda_codec *codec, @@ -13482,11 +13482,11 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, 0x15); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -13511,11 +13511,11 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec) /* Check port replicator headphone socket */ present |= snd_hda_jack_detect(codec, 0x1a); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -13646,11 +13646,11 @@ static void alc269_speaker_automute(struct hda_codec *codec) unsigned char bits; present = snd_hda_jack_detect(codec, nid); - bits = present ? AMP_IN_MUTE(0) : 0; + bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } /* unsolicited event for HP jack sensing */ @@ -17115,9 +17115,9 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) @@ -17128,13 +17128,13 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x21); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) @@ -17145,13 +17145,13 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) present = snd_hda_jack_detect(codec, 0x15); bits = present ? HDA_AMP_MUTE : 0; snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, - AMP_IN_MUTE(0), bits); + HDA_AMP_MUTE, bits); } static void alc662_f5z_speaker_automute(struct hda_codec *codec) @@ -17190,14 +17190,14 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) if (present1 || present2) { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), AMP_IN_MUTE(0)); + HDA_AMP_MUTE, HDA_AMP_MUTE); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), AMP_IN_MUTE(0)); + HDA_AMP_MUTE, HDA_AMP_MUTE); } else { snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, - AMP_IN_MUTE(0), 0); + HDA_AMP_MUTE, 0); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, - AMP_IN_MUTE(0), 0); + HDA_AMP_MUTE, 0); } } -- cgit v1.2.2 From 6694635d3ae1b038d7a0e38b80637db867c7c8e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Mar 2010 17:21:45 +0200 Subject: ALSA: hda - Fix ADC/MUX assignment of ALC269 codec ALC269 codec has a few different variants, and each of them may have different ADC and MUX widgets. For example, one model has ADC 0x08 with MUX 0x23 while others has ADC 0x09 or ADC 0x07 with MUX 022 or 0x24. The difference of ADC appears usually as the capability of the digital mic pin (0x12), and the current driver sometimes misses the internal mic pin due to the mismatching ADC. This patch adds a bit more clever way to find the matching ADC instead of the static list. Now the driver checks all active input pins and fills only the ADC/MUX's that contain all of them. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 95 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 15 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bc55c1e96df5..22aea7b089c6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4984,6 +4984,69 @@ static void set_capture_mixer(struct hda_codec *codec) } } +/* fill adc_nids (and capsrc_nids) containing all active input pins */ +static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, + int num_nids) +{ + struct alc_spec *spec = codec->spec; + int n; + hda_nid_t fallback_adc = 0, fallback_cap = 0; + + for (n = 0; n < num_nids; n++) { + hda_nid_t adc, cap; + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int nconns, i, j; + + adc = nids[n]; + if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN) + continue; + cap = adc; + nconns = snd_hda_get_connections(codec, cap, conn, + ARRAY_SIZE(conn)); + if (nconns == 1) { + cap = conn[0]; + nconns = snd_hda_get_connections(codec, cap, conn, + ARRAY_SIZE(conn)); + } + if (nconns <= 0) + continue; + if (!fallback_adc) { + fallback_adc = adc; + fallback_cap = cap; + } + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (!nid) + continue; + for (j = 0; j < nconns; j++) { + if (conn[j] == nid) + break; + } + if (j >= nconns) + break; + } + if (i >= AUTO_PIN_LAST) { + int num_adcs = spec->num_adc_nids; + spec->private_adc_nids[num_adcs] = adc; + spec->private_capsrc_nids[num_adcs] = cap; + spec->num_adc_nids++; + spec->adc_nids = spec->private_adc_nids; + if (adc != cap) + spec->capsrc_nids = spec->private_capsrc_nids; + } + } + if (!spec->num_adc_nids) { + printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" + " using fallback 0x%x\n", fallback_adc); + spec->private_adc_nids[0] = fallback_adc; + spec->adc_nids = spec->private_adc_nids; + if (fallback_adc != fallback_cap) { + spec->private_capsrc_nids[0] = fallback_cap; + spec->capsrc_nids = spec->private_adc_nids; + } + } +} + #ifdef CONFIG_SND_HDA_INPUT_BEEP #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) @@ -13333,9 +13396,9 @@ static hda_nid_t alc269vb_capsrc_nids[1] = { 0x22, }; -/* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24), - * not a mux! - */ +static hda_nid_t alc269_adc_candidates[] = { + 0x08, 0x09, 0x07, +}; #define alc269_modes alc260_modes #define alc269_capture_source alc880_lg_lw_capture_source @@ -13842,7 +13905,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err; static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; - hda_nid_t real_capsrc_nids; err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, alc269_ignore); @@ -13866,18 +13928,19 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) { add_verb(spec, alc269vb_init_verbs); - real_capsrc_nids = alc269vb_capsrc_nids[0]; alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21); } else { add_verb(spec, alc269_init_verbs); - real_capsrc_nids = alc269_capsrc_nids[0]; alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0); } spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; + fillup_priv_adc_nids(codec, alc269_adc_candidates, + sizeof(alc269_adc_candidates)); + /* set default input source */ - snd_hda_codec_write_cache(codec, real_capsrc_nids, + snd_hda_codec_write_cache(codec, spec->capsrc_nids[0], 0, AC_VERB_SET_CONNECT_SEL, spec->input_mux->items[0].index); @@ -14156,14 +14219,16 @@ static int patch_alc269(struct hda_codec *codec) spec->stream_digital_playback = &alc269_pcm_digital_playback; spec->stream_digital_capture = &alc269_pcm_digital_capture; - if (!is_alc269vb) { - spec->adc_nids = alc269_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); - spec->capsrc_nids = alc269_capsrc_nids; - } else { - spec->adc_nids = alc269vb_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); - spec->capsrc_nids = alc269vb_capsrc_nids; + if (!spec->adc_nids) { /* wasn't filled automatically? use default */ + if (!is_alc269vb) { + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->capsrc_nids = alc269_capsrc_nids; + } else { + spec->adc_nids = alc269vb_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids); + spec->capsrc_nids = alc269vb_capsrc_nids; + } } if (!spec->cap_mixer) -- cgit v1.2.2 From 1f85d72d2c9c9a1d6d32cf325936bc224ad5d591 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Mar 2010 07:48:05 +0200 Subject: ALSA: hda - Add missing printk argument in previous patch Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 22aea7b089c6..ca93c4cc144e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5037,7 +5037,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids, } if (!spec->num_adc_nids) { printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", fallback_adc); + " using fallback 0x%x\n", + codec->chip_name, fallback_adc); spec->private_adc_nids[0] = fallback_adc; spec->adc_nids = spec->private_adc_nids; if (fallback_adc != fallback_cap) { -- cgit v1.2.2 From 5a0e3ad6af8660be21ca98a971cd00f331318c05 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 24 Mar 2010 17:04:11 +0900 Subject: include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo Guess-its-ok-by: Christoph Lameter Cc: Ingo Molnar Cc: Lee Schermerhorn --- sound/pci/ac97/ac97_proc.c | 1 - sound/pci/als4000.c | 1 - sound/pci/aw2/aw2-saa7146.c | 1 - sound/pci/ca0106/ca0106_mixer.c | 1 - sound/pci/ca0106/ca0106_proc.c | 1 - sound/pci/cs5530.c | 1 + sound/pci/cs5535audio/cs5535audio_pcm.c | 1 - sound/pci/cs5535audio/cs5535audio_pm.c | 1 - sound/pci/ctxfi/ctatc.c | 1 + sound/pci/ctxfi/ctpcm.c | 1 + sound/pci/echoaudio/darla20.c | 2 +- sound/pci/echoaudio/darla24.c | 2 +- sound/pci/echoaudio/echo3g.c | 2 +- sound/pci/echoaudio/gina20.c | 2 +- sound/pci/echoaudio/gina24.c | 2 +- sound/pci/echoaudio/indigo.c | 2 +- sound/pci/echoaudio/indigodj.c | 2 +- sound/pci/echoaudio/indigodjx.c | 2 +- sound/pci/echoaudio/indigoio.c | 2 +- sound/pci/echoaudio/indigoiox.c | 2 +- sound/pci/echoaudio/layla20.c | 2 +- sound/pci/echoaudio/layla24.c | 2 +- sound/pci/echoaudio/mia.c | 2 +- sound/pci/echoaudio/mona.c | 2 +- sound/pci/emu10k1/memory.c | 1 + sound/pci/hda/hda_beep.c | 1 + sound/pci/hda/hda_eld.c | 1 + sound/pci/ice1712/ak4xxx.c | 1 + sound/pci/ice1712/amp.c | 1 - sound/pci/ice1712/vt1720_mobo.c | 1 - sound/pci/ice1712/wtm.c | 1 - sound/pci/lx6464es/lx6464es.c | 1 + sound/pci/mixart/mixart.c | 1 + sound/pci/mixart/mixart_hwdep.c | 1 + sound/pci/oxygen/oxygen_lib.c | 1 + sound/pci/rme32.c | 2 +- sound/pci/rme96.c | 1 - sound/pci/rme9652/hdsp.c | 1 - sound/pci/rme9652/rme9652.c | 1 - sound/pci/sis7019.c | 1 + 40 files changed, 27 insertions(+), 28 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 73b17d526c8b..6320bf084e47 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -22,7 +22,6 @@ * */ -#include #include #include diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index d75cf7b06426..6cf1de8042e8 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -68,7 +68,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c index 296123ab74f7..8afd8b5d1ac7 100644 --- a/sound/pci/aw2/aw2-saa7146.c +++ b/sound/pci/aw2/aw2-saa7146.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 8f443a9d61ec..85fd315d9999 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 0470461cc03e..ba96428c9f4c 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 207479a641cf..bc07e275d4d4 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 0f48a871f17b..f16bc8aad6ed 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -23,7 +23,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 564c33b60953..a3301cc4ab82 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 480cb1e905b6..1bff80cde0a2 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -24,6 +24,7 @@ #include "ctdaio.h" #include "cttimer.h" #include +#include #include #include #include diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index d0dc227fbdd3..85ab43e89212 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c @@ -17,6 +17,7 @@ #include "ctpcm.h" #include "cttimer.h" +#include #include /* Hardware descriptions for playback */ diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index a65bafe0800f..fe7ad64dccd7 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -40,9 +40,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index 0a6c50bcd758..d1fd34b1a8e3 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -44,9 +44,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index f5142796989b..1dffdc54416d 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -51,9 +51,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index 2364f8a1bc21..050e54aa693f 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -44,9 +44,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index 616b55825a19..5748fc6d29d6 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -50,9 +50,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 776175c0bdad..4ae5e35cb5f1 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 8816b0bd2ba6..3550715bab1c 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c index b1e3652f2f48..19b191fd0120 100644 --- a/sound/pci/echoaudio/indigodjx.c +++ b/sound/pci/echoaudio/indigodjx.c @@ -42,10 +42,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index 1035125336d6..a9fcedf317a4 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -43,9 +43,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c index 60b7cb2753cf..bcdfac63212c 100644 --- a/sound/pci/echoaudio/indigoiox.c +++ b/sound/pci/echoaudio/indigoiox.c @@ -43,10 +43,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 8c3f5c5b5301..d3a98c5dac86 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -49,9 +49,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index ed1cc0abc2b8..2a1dca6dce17 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -51,9 +51,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index cc2bbfc65327..9cdf14cfdd74 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -50,9 +50,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 3e7e01824b40..1047be405ebe 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -48,9 +48,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 6a47672f930a..ffb1ddb8dc28 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -22,6 +22,7 @@ */ #include +#include #include #include diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e4581a42ace5..29714c818b53 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include "hda_beep.h" diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index dcd22446cfc7..d8da18a9e98b 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include "hda_codec.h" diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c index 03391da8c8c7..90d560c3df13 100644 --- a/sound/pci/ice1712/ak4xxx.c +++ b/sound/pci/ice1712/ak4xxx.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 6da21a2bcade..e328cfb7620c 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 7f9674b641c0..4c551e147c08 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index 5af9e84456d1..e618f789026e 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "ice1712.h" diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 0cca56038cd9..ef9af3f4ace2 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7e8e7da592a9..55e9315d4ccd 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 4cf4cd8c939c..bf2696aa5d49 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "mixart.h" diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 9c5e6450eebb..fad03d64e3ad 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index d5e1c6eb7b7b..3c04524de37c 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -70,10 +70,10 @@ #include +#include #include #include #include -#include #include #include diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 9d5252bc870c..d19dc052c391 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 52c6eb57cc3f..b92adef8e81e 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 44a3e2d8c556..c492af5b25f3 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 7e3e8fbc90fe..9cc1b5aa0148 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.2 From b8e80cf386419453678b01bef830f53445ebb15d Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Tue, 30 Mar 2010 13:29:28 -0400 Subject: ALSA: hda: Fix 0 dB offset for Lenovo Thinkpad models using AD1981 BugLink: https://launchpad.net/bugs/551606 The OR's hardware distorts at PCM 100% because it does not correspond to 0 dB. Fix this in patch_ad1981() for all models using the Thinkpad quirk. Reported-by: Jane Silber Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index e6d1bdff1b6e..af34606c30c3 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1896,6 +1896,14 @@ static int patch_ad1981(struct hda_codec *codec) case AD1981_THINKPAD: spec->mixers[0] = ad1981_thinkpad_mixers; spec->input_mux = &ad1981_thinkpad_capture_source; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1981_TOSHIBA: spec->mixers[0] = ad1981_hp_mixers; -- cgit v1.2.2 From 3815595e78d2baae6feb866e737f92d8ef48b337 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 4 Apr 2010 12:14:03 +0200 Subject: ALSA: hda - Add MSI blacklist for Aopen MZ915-M The device needs MSI disablement. Added to the quirk list. Reported-by: Harald Dunkel Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4bb90675f70f..f8fd586ae024 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2362,6 +2362,7 @@ static struct snd_pci_quirk msi_black_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ + SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ {} }; -- cgit v1.2.2 From a0fd4345f928d72a56e27b23e4cd28c94bf36be5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 2 Apr 2010 14:47:59 +0200 Subject: ALSA: echoaudio - Eliminate use after free Use the call to snd_card_free in the error handling code at the end of the function, as in the other error cases. A simplified version of the semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E,E2; @@ snd_card_free(E) ... ( E = E2 | * E ) // Signed-off-by: Julia Lawall Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 8dab82d7d19d..668a5ec04499 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2184,10 +2184,9 @@ static int __devinit snd_echo_probe(struct pci_dev *pci, goto ctl_error; #endif - if ((err = snd_card_register(card)) < 0) { - snd_card_free(card); + err = snd_card_register(card); + if (err < 0) goto ctl_error; - } snd_printk(KERN_INFO "Card registered: %s\n", card->longname); pci_set_drvdata(pci, chip); -- cgit v1.2.2 From d12841827a6de120199609dadb6ff4ec99bd90ea Mon Sep 17 00:00:00 2001 From: Tony Vroon Date: Mon, 5 Apr 2010 16:30:43 +0100 Subject: ALSA: hda - Enable amplifiers on Acer Inspire 6530G After more tests it appears that EAPD needs to be enabled on both the 0x14 and 0x15 NIDs to enable the main speaker and headphone amplifiers. The maximum volume setting is now equal to what the machine achieves under other operating systems. Disabling Front or LFE playback triggers EAPD and disables the amplifier. As such, these two playback switches have been removed from the mixer. Signed-off-by: Tony Vroon Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ca93c4cc144e..547206296d7b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1621,6 +1621,11 @@ static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { */ static struct hda_verb alc888_acer_aspire_6530g_verbs[] = { +/* Route to built-in subwoofer as well as speakers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* Bias voltage on for external mic port */ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80}, /* Front Mic: set to PIN_IN (empty by default) */ @@ -1632,10 +1637,12 @@ static struct hda_verb alc888_acer_aspire_6530g_verbs[] = { /* Enable speaker output */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, /* Enable headphone output */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, { } }; @@ -8462,9 +8469,7 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("LFE Playback Switch", 0x0f, 2, 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("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), -- cgit v1.2.2 From f9700d5a4575e7fb343df10a1d29d425e4b81082 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 5 Apr 2010 23:25:13 +0200 Subject: ALSA: hda - Fix a wrong array range check in patch_realtek.c The commit 6a4f2ccb467e00281470cde2dee08fe5ecde62d1 introduced a wrong comparision for the array range check, which effectively skips the whole initialization of DAC connections. Fixed now. Reference: bko#15689 https://bugzilla.kernel.org/show_bug.cgi?id=15689 Reported-by: Adrian Ulrich Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 547206296d7b..c7730dbb9ddb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10110,13 +10110,12 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, int idx; alc_set_pin_output(codec, nid, pin_type); + if (dac_idx >= spec->multiout.num_dacs) + return; if (spec->multiout.dac_nids[dac_idx] == 0x25) idx = 4; - else { - if (spec->multiout.num_dacs >= dac_idx) - return; + else idx = spec->multiout.dac_nids[dac_idx] - 2; - } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); } -- cgit v1.2.2 From b0cc58a25d04160d39a80e436847eaa2fbc5aa09 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 6 Apr 2010 19:31:26 +0300 Subject: ALSA: mixart: range checking proc file The original code doesn't take into consideration that the value of MIXART_BA0_SIZE - pos can be less than zero which would lead to a large unsigned value for "count". Also I moved the check that read size is a multiple of 4 bytes below the code that adjusts "count". Signed-off-by: Dan Carpenter Cc: Acked-by: Linus Torvalds Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 7e8e7da592a9..ea4256b08a38 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1161,13 +1161,15 @@ static long snd_mixart_BA0_read(struct snd_info_entry *entry, void *file_private unsigned long count, unsigned long pos) { struct mixart_mgr *mgr = entry->private_data; + unsigned long maxsize; - count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ - if(count <= 0) + if (pos >= MIXART_BA0_SIZE) return 0; - if(pos + count > MIXART_BA0_SIZE) - count = (long)(MIXART_BA0_SIZE - pos); - if(copy_to_user_fromio(buf, MIXART_MEM( mgr, pos ), count)) + maxsize = MIXART_BA0_SIZE - pos; + if (count > maxsize) + count = maxsize; + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count)) return -EFAULT; return count; } @@ -1180,13 +1182,15 @@ static long snd_mixart_BA1_read(struct snd_info_entry *entry, void *file_private unsigned long count, unsigned long pos) { struct mixart_mgr *mgr = entry->private_data; + unsigned long maxsize; - count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ - if(count <= 0) + if (pos > MIXART_BA1_SIZE) return 0; - if(pos + count > MIXART_BA1_SIZE) - count = (long)(MIXART_BA1_SIZE - pos); - if(copy_to_user_fromio(buf, MIXART_REG( mgr, pos ), count)) + maxsize = MIXART_BA1_SIZE - pos; + if (count > maxsize) + count = maxsize; + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count)) return -EFAULT; return count; } -- cgit v1.2.2 From 7ad7b218f4aae4f395b3b4cef261572556bbd20a Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Tue, 6 Apr 2010 18:12:52 +0200 Subject: ALSA: hda: Add support for Medion WIM2160 This adds support for the Medion WIM2160 soundcard. There's no PCI quirk added because it has the same PCI id as the Medion MD2. Signed-off-by: Maurus Cuelenaere Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c7730dbb9ddb..2971e48e50ad 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -230,6 +230,7 @@ enum { ALC888_ACER_ASPIRE_7730G, ALC883_MEDION, ALC883_MEDION_MD2, + ALC883_MEDION_WIM2160, ALC883_LAPTOP_EAPD, ALC883_LENOVO_101E_2ch, ALC883_LENOVO_NB0763, @@ -8455,6 +8456,42 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_medion_wim2160_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc883_medion_wim2160_verbs[] = { + /* Unmute front mixer */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* Set speaker pin to front mixer */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* Init headphone pin */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + + { } /* end */ +}; + +/* toggle speaker-output according to the hp-jack state */ +static void alc883_medion_wim2160_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + spec->autocfg.hp_pins[0] = 0x1a; + spec->autocfg.speaker_pins[0] = 0x15; +} + 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), @@ -9164,6 +9201,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g", [ALC883_MEDION] = "medion", [ALC883_MEDION_MD2] = "medion-md2", + [ALC883_MEDION_WIM2160] = "medion-wim2160", [ALC883_LAPTOP_EAPD] = "laptop-eapd", [ALC883_LENOVO_101E_2ch] = "lenovo-101e", [ALC883_LENOVO_NB0763] = "lenovo-nb0763", @@ -9818,6 +9856,21 @@ static struct alc_config_preset alc882_presets[] = { .setup = alc883_medion_md2_setup, .init_hook = alc_automute_amp, }, + [ALC883_MEDION_WIM2160] = { + .mixers = { alc883_medion_wim2160_mixer }, + .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_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, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc_automute_amp_unsol_event, + .setup = alc883_medion_wim2160_setup, + .init_hook = alc_automute_amp, + }, [ALC883_LAPTOP_EAPD] = { .mixers = { alc883_base_mixer }, .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, -- cgit v1.2.2 From 531d8791accf1464bc6854ff69d08dd866189d17 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 9 Apr 2010 10:57:33 +0200 Subject: ALSA: hda - Fix auto-parser of ALC269vb for HP pin NID 0x21 ALC269vb has an alternative HP pin 0x21 in addition. Fix the parser to recognize it. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2971e48e50ad..fbbdfbc8a1ca 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12869,6 +12869,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, dac = 0x02; break; case 0x15: + case 0x21: /* ALC269vb has this pin, too */ dac = 0x03; break; default: -- cgit v1.2.2 From 226b1ec8c18bcb6d1aa448a29b2c8aeae1946228 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 9 Apr 2010 11:01:20 +0200 Subject: ALSA: hda - Fix setup for ALC269vb amic and dmic models Corrected HP and mic pins for ALC269vb amic and dmic models. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbbdfbc8a1ca..9b58f29833e6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -13789,19 +13789,19 @@ static void alc269_laptop_unsol_event(struct hda_codec *codec, } } -static void alc269_laptop_dmic_setup(struct hda_codec *codec) +static void alc269_laptop_amic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; - spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 5; + spec->int_mic.pin = 0x19; + spec->int_mic.mux_idx = 1; spec->auto_mic = 1; } -static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +static void alc269_laptop_dmic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; spec->autocfg.hp_pins[0] = 0x15; @@ -13809,14 +13809,14 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; spec->int_mic.pin = 0x12; - spec->int_mic.mux_idx = 6; + spec->int_mic.mux_idx = 5; spec->auto_mic = 1; } -static void alc269_laptop_amic_setup(struct hda_codec *codec) +static void alc269vb_laptop_amic_setup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - spec->autocfg.hp_pins[0] = 0x15; + spec->autocfg.hp_pins[0] = 0x21; spec->autocfg.speaker_pins[0] = 0x14; spec->ext_mic.pin = 0x18; spec->ext_mic.mux_idx = 0; @@ -13825,6 +13825,18 @@ static void alc269_laptop_amic_setup(struct hda_codec *codec) spec->auto_mic = 1; } +static void alc269vb_laptop_dmic_setup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + spec->autocfg.hp_pins[0] = 0x21; + spec->autocfg.speaker_pins[0] = 0x14; + spec->ext_mic.pin = 0x18; + spec->ext_mic.mux_idx = 0; + spec->int_mic.pin = 0x12; + spec->int_mic.mux_idx = 6; + spec->auto_mic = 1; +} + static void alc269_laptop_inithook(struct hda_codec *codec) { alc269_speaker_automute(codec); @@ -14162,7 +14174,7 @@ static struct alc_config_preset alc269_presets[] = { .num_channel_mode = ARRAY_SIZE(alc269_modes), .channel_mode = alc269_modes, .unsol_event = alc269_laptop_unsol_event, - .setup = alc269_laptop_amic_setup, + .setup = alc269vb_laptop_amic_setup, .init_hook = alc269_laptop_inithook, }, [ALC269VB_DMIC] = { -- cgit v1.2.2 From 7f311a46916a3be00a1a8e3f1bdf461d08f1d263 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 9 Apr 2010 17:32:23 +0200 Subject: ALSA: hda - Fix initial capture source connections of ALC880/260 The widget connections of ADC of ALC880 and ALC2260 aren't initialized, thus it might point to invalid pin. This can be a problem when mode=auto and there is only one input pin. Then user can't change the connection at all. This patch adds the code to initialize the input pin connection of these codecs. Reference: Novell bnc#594363 https://bugzilla.novell.com/show_bug.cgi?id=594363 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9b58f29833e6..8d60b1f25ce1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4809,6 +4809,25 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) } } +static void alc880_auto_init_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int c; + + for (c = 0; c < spec->num_adc_nids; c++) { + unsigned int mux_idx; + const struct hda_input_mux *imux; + mux_idx = c >= spec->num_mux_defs ? 0 : c; + imux = &spec->input_mux[mux_idx]; + if (!imux->num_items && mux_idx > 0) + imux = &spec->input_mux[0]; + if (imux) + snd_hda_codec_write(codec, spec->adc_nids[c], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } +} + /* parse the BIOS configuration and set up the alc_spec */ /* return 1 if successful, 0 if the proper config is not found, * or a negative error code @@ -4887,6 +4906,7 @@ static void alc880_auto_init(struct hda_codec *codec) alc880_auto_init_multi_out(codec); alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); + alc880_auto_init_input_src(codec); if (spec->unsol_event) alc_inithook(codec); } @@ -6398,6 +6418,8 @@ static void alc260_auto_init_analog_input(struct hda_codec *codec) } } +#define alc260_auto_init_input_src alc880_auto_init_input_src + /* * generic initialization of ADC, input mixers and output mixers */ @@ -6484,6 +6506,7 @@ 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); + alc260_auto_init_input_src(codec); if (spec->unsol_event) alc_inithook(codec); } -- cgit v1.2.2 From 7fa90e873f520dad5ec58f47340996cda083e875 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Apr 2010 08:49:00 +0200 Subject: ALSA: hda - Enhance fix-up table for Realtek codecs A few enhancement / fixes for fix-up table of some Realtek codecs: - Apply fix-ups only for the auto model - Apply additional verbs after normal init verbs - Add a debug print to show the fix-up application This is basically a preliminary work for the next fix for Sony VAIO. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8d60b1f25ce1..cff57710d1fb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1390,22 +1390,31 @@ struct alc_fixup { static void alc_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, - const struct alc_fixup *fix) + const struct alc_fixup *fix, + int pre_init) { const struct alc_pincfg *cfg; quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); if (!quirk) return; - fix += quirk->value; cfg = fix->pins; - if (cfg) { + if (pre_init && cfg) { +#ifdef CONFIG_SND_DEBUG_VERBOSE + snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n", + codec->chip_name, quirk->name); +#endif for (; cfg->nid; cfg++) snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); } - if (fix->verbs) + if (!pre_init && fix->verbs) { +#ifdef CONFIG_SND_DEBUG_VERBOSE + snd_printdd(KERN_INFO "hda_codec: %s: Apply fix-verbs for %s\n", + codec->chip_name, quirk->name); +#endif add_verb(codec->spec, fix->verbs); + } } static int alc_read_coef_idx(struct hda_codec *codec, @@ -10439,7 +10448,8 @@ static int patch_alc882(struct hda_codec *codec) board_config = ALC882_AUTO; } - alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups); + if (board_config == ALC882_AUTO) + alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1); if (board_config == ALC882_AUTO) { /* automatic parse from the BIOS config */ @@ -10512,6 +10522,9 @@ static int patch_alc882(struct hda_codec *codec) set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (board_config == ALC882_AUTO) + alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0); + spec->vmaster_nid = 0x0c; codec->patch_ops = alc_patch_ops; @@ -15417,7 +15430,8 @@ static int patch_alc861(struct hda_codec *codec) board_config = ALC861_AUTO; } - alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups); + if (board_config == ALC861_AUTO) + alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 1); if (board_config == ALC861_AUTO) { /* automatic parse from the BIOS config */ @@ -15454,6 +15468,9 @@ static int patch_alc861(struct hda_codec *codec) spec->vmaster_nid = 0x03; + if (board_config == ALC861_AUTO) + alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 0); + codec->patch_ops = alc_patch_ops; if (board_config == ALC861_AUTO) { spec->init_hook = alc861_auto_init; @@ -16388,7 +16405,8 @@ static int patch_alc861vd(struct hda_codec *codec) board_config = ALC861VD_AUTO; } - alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups); + if (board_config == ALC861VD_AUTO) + alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 1); if (board_config == ALC861VD_AUTO) { /* automatic parse from the BIOS config */ @@ -16436,6 +16454,9 @@ static int patch_alc861vd(struct hda_codec *codec) spec->vmaster_nid = 0x02; + if (board_config == ALC861VD_AUTO) + alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 0); + codec->patch_ops = alc_patch_ops; if (board_config == ALC861VD_AUTO) -- cgit v1.2.2 From ff818c24c2af370153646d302d831b69b023816f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Apr 2010 08:59:25 +0200 Subject: ALSA: hda - Add fix-up for Sony VAIO with ALC269 Sony VAIO models with ALC269 need to initialize the pin 0x19 to VREF ground or Hi-Z to make the headphone working. Other than that, model=auto works fine, so let's use model=auto with a specific fix-up table. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cff57710d1fb..4b35176d3454 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14077,6 +14077,27 @@ static void alc269_auto_init(struct hda_codec *codec) alc_inithook(codec); } +enum { + ALC269_FIXUP_SONY_VAIO, +}; + +const static struct hda_verb alc269_sony_vaio_fixup_verbs[] = { + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, + {} +}; + +static const struct alc_fixup alc269_fixups[] = { + [ALC269_FIXUP_SONY_VAIO] = { + .verbs = alc269_sony_vaio_fixup_verbs + }, +}; + +static struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), + {} +}; + + /* * configuration and preset */ @@ -14136,7 +14157,7 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = { ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x104d, 0x9071, "SONY XTB", ALC269_DMIC), + SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), @@ -14290,6 +14311,9 @@ static int patch_alc269(struct hda_codec *codec) board_config = ALC269_AUTO; } + if (board_config == ALC269_AUTO) + alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 1); + if (board_config == ALC269_AUTO) { /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); @@ -14342,6 +14366,9 @@ static int patch_alc269(struct hda_codec *codec) set_capture_mixer(codec); set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + if (board_config == ALC269_AUTO) + alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0); + spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; -- cgit v1.2.2 From b331439dfd41dc813b3557ca5927a3a644f35792 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 14 Apr 2010 14:33:57 +0200 Subject: ALSA: hda - Fix control element allocations in VIA codec parser The commit 5b0cb1d850c26893b1468b3a519433a1b7a176be ALSA: hda - add more NID->Control mapping breaks the control element allocation by returning a wrong value. Let's fix it. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 9ddc37300f6b..be1295438989 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -476,7 +476,7 @@ static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, knew->name = kstrdup(tmpl->name, GFP_KERNEL); if (!knew->name) return NULL; - return 0; + return knew; } static void via_free_kctls(struct hda_codec *codec) -- cgit v1.2.2 From 3d83e577a8206f0f3822a3840e12f76477142ba2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 14 Apr 2010 14:36:23 +0200 Subject: ALSA: hda - Avoid invalid "Independent HP" control for VIA codecs Some VIA codecs have no multiple source selection for headphone pins, thus it's useless (and wrong) to create "Independent HP" control on them. This patch adds the check of connections to skip the control in such a case. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_via.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index be1295438989..73453814e098 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1215,14 +1215,13 @@ static struct snd_kcontrol_new via_hp_mixer[2] = { }, }; -static int via_hp_build(struct via_spec *spec) +static int via_hp_build(struct hda_codec *codec) { + struct via_spec *spec = codec->spec; struct snd_kcontrol_new *knew; hda_nid_t nid; - - knew = via_clone_control(spec, &via_hp_mixer[0]); - if (knew == NULL) - return -ENOMEM; + int nums; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; switch (spec->codec_type) { case VT1718S: @@ -1239,6 +1238,14 @@ static int via_hp_build(struct via_spec *spec) break; } + nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); + if (nums <= 1) + return 0; + + knew = via_clone_control(spec, &via_hp_mixer[0]); + if (knew == NULL) + return -ENOMEM; + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; knew->private_value = nid; @@ -2561,7 +2568,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); return 1; @@ -3087,7 +3094,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); return 1; @@ -3654,7 +3661,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); return 1; @@ -4140,7 +4147,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); return 1; @@ -4510,7 +4517,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); return 1; } @@ -4930,7 +4937,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); @@ -5425,7 +5432,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); via_smart51_build(spec); @@ -5781,7 +5788,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); return 1; } @@ -6000,12 +6007,12 @@ static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, /* Line-Out: PortE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Master Front Playback Volume", + "Front Playback Volume", HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, - "Master Front Playback Switch", + "Front Playback Switch", HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); if (err < 0) return err; @@ -6130,7 +6137,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec) spec->input_mux = &spec->private_imux[0]; if (spec->hp_mux) - via_hp_build(spec); + via_hp_build(codec); return 1; } -- cgit v1.2.2 From d1501ea844eefdf925f6b711875b4b2b928fddf8 Mon Sep 17 00:00:00 2001 From: Joerg Schirottke Date: Thu, 15 Apr 2010 08:37:41 +0200 Subject: ALSA: hda - add a quirk for Clevo M570U laptop Added the matching model for Clevo laptop M570U. Signed-off-by: Joerg Schirottke Tested-by: Maximilian Gerhard Cc: Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4b35176d3454..aad1627f56f1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9350,6 +9350,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_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, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R), -- cgit v1.2.2 From 8815cd030fdd73932a791d1f06194c8db807cde7 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 15 Apr 2010 09:02:41 +0200 Subject: ALSA: hda - Add position_fix quirk for Biostar mobo The Biostar mobo seems to give a wrong DMA position, resulting in stuttering or skipping sounds on 2.6.34. Since the commit 7b3a177b0d4f92b3431b8dca777313a07533a710, "ALSA: pcm_lib: fix "something must be really wrong" condition", makes the position check more strictly, the DMA position problem is revealed more clearly now. The fix is to use only LPIB for obtaining the position, i.e. passing position_fix=1. This patch adds a static quirk to achieve it as default. Reported-by: Frank Griffin Cc: Eric Piel Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f8fd586ae024..f669442b7c82 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2272,6 +2272,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB), SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), {} }; -- cgit v1.2.2 From b7d2526f5c20385894a5e57b1a4292f5a1741f1b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 19 Apr 2010 18:11:29 +0200 Subject: ALSA: hda - Fix resume from StR of HP 2510p with docking-station When HP laptop with AD1981 codec is suspended and the docking-station is connected before the resume, the outputs get confused, and wrongly routed still to the speaker. This is because of a change in 2.6.34-rc1 ea52bf260ecbb175339af3178c15788df21b7516 ALSA: hda: Add powerdown for Analog Devices HDA codecs The problem was the added resume callback that doesn't consider the modified init hook. The fix is simply remove the resume callback here and make the resume normally. This doesn't change any behavior intended in the commit above (for shutting down the sound at suspend) but only fixes the resume. Reported-and-tested-by: Frans Pop Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index af34606c30c3..e9fdfc4b1c57 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -519,14 +519,6 @@ static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) ad198x_power_eapd(codec); return 0; } - -static int ad198x_resume(struct hda_codec *codec) -{ - ad198x_init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - return 0; -} #endif static struct hda_codec_ops ad198x_patch_ops = { @@ -539,7 +531,6 @@ static struct hda_codec_ops ad198x_patch_ops = { #endif #ifdef SND_HDA_NEEDS_RESUME .suspend = ad198x_suspend, - .resume = ad198x_resume, #endif .reboot_notify = ad198x_shutup, }; -- cgit v1.2.2 From aac78daf8f37256283f56820ae858add7139c56c Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 21 Apr 2010 20:41:52 -0400 Subject: ALSA: hda: Use STAC_DELL_M6_BOTH quirk for Dell Studio XPS 1645 BugLink: https://launchpad.net/bugs/553002 The OR has verified that the dell-m6 model quirk is necessary for audio to be audible by default on the Dell Studio XPS 1645. This change is necessary for 2.6.32.11 and 2.6.33.2 alike. Reported-by: Robert Chambers Tested-by: Robert Chambers Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c4be3fab94e5..81ecd9388a80 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1607,6 +1607,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { "Dell Studio 1555", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd, "Dell Studio 1557", STAC_DELL_M6_DMIC), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, + "Dell Studio XPS 1645", STAC_DELL_M6_BOTH), {} /* terminator */ }; -- cgit v1.2.2 From 3353541fe533350a22a03e2fb7dc085b35912575 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Thu, 22 Apr 2010 07:15:26 -0400 Subject: ALSA: hda: Use ALC880_F1734 quirk for Fujitsu Siemens AMILO Xi 1526 BugLink: https://launchpad.net/bugs/567494 The OR has verified that the existing model quirk, ALC880_UNIWILL, is insufficient for audible playback and capture by default. Instead, the ALC880_F1734 model quirk needs to be used. This change is necessary for both 2.6.32.11 and 2.6.33.2. Reported-by: Arnaud Malpeyre Tested-by: Arnaud Malpeyre Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index aad1627f56f1..7404dba16f83 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4143,7 +4143,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734), SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), -- cgit v1.2.2 From 7efbfd1ae98ef9efe06352e2a1ad83e8c14ceeb1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 21 Apr 2010 11:04:06 -0400 Subject: ALSA: snd-meastro3: Add amp_gpio quirk for Compaq EVO N600C Without this quirk sound stops working after suspend resume. With this quirk, one still needs to manually unmute the master volume control after a suspend / / resume cycle. That is fixed in another patch in this set. Note that this patch was submitted to the alsa bug tracker a long time ago: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4319 Signed-off-by: Hans de Goede CC: Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index b64e78139d63..728de232e091 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -884,6 +884,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_m3_ids) = { MODULE_DEVICE_TABLE(pci, snd_m3_ids); static struct snd_pci_quirk m3_amp_quirk_list[] __devinitdata = { + SND_PCI_QUIRK(0x0E11, 0x0094, "Compaq Evo N600c", 0x0c), SND_PCI_QUIRK(0x10f7, 0x833e, "Panasonic CF-28", 0x0d), SND_PCI_QUIRK(0x10f7, 0x833d, "Panasonic CF-72", 0x0d), SND_PCI_QUIRK(0x1033, 0x80f1, "NEC LM800J/7", 0x03), -- cgit v1.2.2 From 715aa675338ce6e1a3b4f77cf87ea611f93058a8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 21 Apr 2010 11:04:08 -0400 Subject: ALSA: snd-meastro3: Ignore spurious HV interrupts during suspend / resume Ignore spurious HV interrupts during suspend / resume, this avoids mistaking them for a mute button press. This is not very pretty but it seems the only way to fix the master volume control gets muted after suspend issue I'm seeing. Note that the es1968 driver is doing exactly the same. Signed-off-by: Hans de Goede Cc: Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 728de232e091..b56e33676780 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -849,6 +849,7 @@ struct snd_m3 { struct snd_kcontrol *master_switch; struct snd_kcontrol *master_volume; struct tasklet_struct hwvol_tq; + unsigned int in_suspend; #ifdef CONFIG_PM u16 *suspend_mem; @@ -1614,6 +1615,11 @@ static void snd_m3_update_hw_volume(unsigned long private_data) outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER); outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER); + /* Ignore spurious HV interrupts during suspend / resume, this avoids + mistaking them for a mute button press. */ + if (chip->in_suspend) + return; + if (!chip->master_switch || !chip->master_volume) return; @@ -2425,6 +2431,7 @@ static int m3_suspend(struct pci_dev *pci, pm_message_t state) if (chip->suspend_mem == NULL) return 0; + chip->in_suspend = 1; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); @@ -2498,6 +2505,7 @@ static int m3_resume(struct pci_dev *pci) snd_m3_hv_init(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); + chip->in_suspend = 0; return 0; } #endif /* CONFIG_PM */ -- cgit v1.2.2 From 0e0280dc2b0c7395a880d25544b47f3e3e3f79db Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 21 Apr 2010 19:55:43 -0400 Subject: ALSA: hda: Use LPIB quirk for DG965OT board version AAD63733-203 BugLink: https://launchpad.net/bugs/459083 The OR has verified with 2.6.32.11 and the latest alsa-driver stable daily snapshot that position_fix=1 is necessary for the external mic to work and for PulseAudio not to crash constantly. This patch is necessary also for 2.6.32.11 and 2.6.33.2. Reported-by: Tested-by: Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index f669442b7c82..cec68152dcb1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2273,6 +2273,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB), SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB), + SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB), SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB), {} }; -- cgit v1.2.2 From 5c1bccf645d4ab65e4c7502acb42e8b9afdb5bdc Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Thu, 22 Apr 2010 17:54:45 -0400 Subject: ALSA: hda: Use STAC_DELL_M6_BOTH quirk for Dell Studio 1558 BugLink: https://launchpad.net/bugs/568600 The OR has verified that the dell-m6 model quirk is necessary for audio to be audible by default on the Dell Studio XPS 1645. This change is necessary for 2.6.32.11 and 2.6.33.2 alike. Reported-by: Andy Ross Tested-by: Andy Ross Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 81ecd9388a80..7fb7d017a347 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1609,6 +1609,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { "Dell Studio 1557", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, "Dell Studio XPS 1645", STAC_DELL_M6_BOTH), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, + "Dell Studio 1558", STAC_DELL_M6_BOTH), {} /* terminator */ }; -- cgit v1.2.2 From 8dd34ab111dc6ccb35a1a7a59222cb9bb0160e6f Mon Sep 17 00:00:00 2001 From: "Brian J. Tarricone" Date: Sun, 2 May 2010 17:32:10 -0700 Subject: ALSA: hda - fix array indexing while creating inputs for Cirrus codecs This fixes a problem where cards show up as only having a single mixer element, suppressing all sound output. Signed-off-by: Brian J. Tarricone Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_cirrus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 7de782a5b8f4..350ee8ac4153 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -766,7 +766,7 @@ static int build_input(struct hda_codec *codec) for (n = 0; n < AUTO_PIN_LAST; n++) { if (!spec->adc_nid[n]) continue; - err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[i]); + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); if (err < 0) return err; } -- cgit v1.2.2 From 4442dd4613fe3795b4c8a5f42fc96b7ffb90d01a Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Mon, 3 May 2010 20:39:31 -0400 Subject: ALSA: hda: Use olpc-xo-1_5 quirk for Toshiba Satellite Pro T130-15F BugLink: https://launchpad.net/bugs/573284 The OR verified that using the olpc-xo-1_5 model quirk allows the headphones to be audible when inserted into the jack. Capture was also verified to work correctly. Reported-by: Andy Couldrake Tested-by: Andy Couldrake Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 61682e1d09da..e1323e45f124 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2842,6 +2842,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} }; -- cgit v1.2.2 From c53666813813a0ea3d0391e1911eefc05a5e6b4f Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Tue, 4 May 2010 22:07:58 -0400 Subject: ALSA: hda: Use olpc-xo-1_5 quirk for Toshiba Satellite P500-PSPGSC-01800T BugLink: https://launchpad.net/bugs/549267 The OR verified that using the olpc-xo-1_5 model quirk allows the headphones to be audible when inserted into the jack. Capture was also verified to work correctly. Reported-by: Richard Gagne Tested-by: Richard Gagne Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e1323e45f124..924c122f16fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2842,6 +2842,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} -- cgit v1.2.2 From 231f50bc0e9735fd1b3fd376a8d3b6a14aee0694 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Wed, 28 Apr 2010 18:05:06 +0200 Subject: ALSA: hda - Add quirk for Dell Inspiron 19T using a Conexant CX20582 Add a quirk for all-in-one computer Dell Inspiron One 19 Touch to have proper HP and Mic support. Signed-off-by: Anisse Astier Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 924c122f16fa..e2b698b721db 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2842,6 +2842,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { CXT5066_DELL_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), + SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), -- cgit v1.2.2 From 8f0f5ff6777104084b4b2e1ae079541c2a6ed6d9 Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Wed, 28 Apr 2010 18:00:11 -0400 Subject: ALSA: hda: Fix 0 dB for Packard Bell models using Conexant CX20549 (Venice) BugLink: https://launchpad.net/bugs/541802 The OR's hardware distorts at PCM 100% because it does not correspond to 0 dB. Fix this in patch_cxt5045() for all Packard Bell models. Reported-by: Valombre Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e2b698b721db..56e52071c769 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1195,9 +1195,10 @@ static int patch_cxt5045(struct hda_codec *codec) switch (codec->subsystem_id >> 16) { case 0x103c: + case 0x1631: case 0x1734: - /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB - * on NID 0x17. Fix max PCM level to 0 dB + /* HP, Packard Bell, & Fujitsu-Siemens laptops have really bad + * sound over 0dB on NID 0x17. Fix max PCM level to 0 dB * (originally it has 0x2b steps with 0dB offset 0x14) */ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, -- cgit v1.2.2 From 4d26f44657915f082806abfe3624aeded4c121fa Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 7 May 2010 08:47:54 +0800 Subject: ALSA: hda - fix DG45ID SPDIF output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts part of commit 52dc438606d1e, in order to fix a regression: broken SPDIF output on Intel DG45FC motherboard (IDT 92HD73E1X5 codec). --- DG45FC-IDT-codec-2.6.32 (SPDIF OK) +++ DG45FC-IDT-codec-2.6.33 (SPDIF broken) Node 0x22 [Pin Complex] wcaps 0x400301: Stereo Digital Pincap 0x00000010: OUT - Pin Default 0x40f000f0: [N/A] Other at Ext N/A - Conn = Unknown, Color = Unknown - DefAssociation = 0xf, Sequence = 0x0 - Pin-ctls: 0x00: + Pin Default 0x014510a0: [Jack] SPDIF Out at Ext Rear + Conn = Optical, Color = Black + DefAssociation = 0xa, Sequence = 0x0 + Pin-ctls: 0x40: OUT Connection: 3 0x25* 0x20 0x21 Node 0x23 [Pin Complex] wcaps 0x400301: Stereo Digital Pincap 0x00000010: OUT - Pin Default 0x01451140: [Jack] SPDIF Out at Ext Rear + Pin Default 0x074510b0: [Jack] SPDIF Out at Ext Rear Panel Conn = Optical, Color = Black - DefAssociation = 0x4, Sequence = 0x0 - Misc = NO_PRESENCE - Pin-ctls: 0x40: OUT + DefAssociation = 0xb, Sequence = 0x0 + Pin-ctls: 0x00: Connection: 3 0x26* 0x20 0x21 Cc: Cc: Alexey Fisher Tested-by: David Härdeman Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 7fb7d017a347..12825aa03106 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1544,11 +1544,9 @@ static unsigned int alienware_m17x_pin_configs[13] = { 0x904601b0, }; -static unsigned int intel_dg45id_pin_configs[14] = { +static unsigned int intel_dg45id_pin_configs[13] = { 0x02214230, 0x02A19240, 0x01013214, 0x01014210, - 0x01A19250, 0x01011212, 0x01016211, 0x40f000f0, - 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x014510A0, - 0x074510B0, 0x40f000f0 + 0x01A19250, 0x01011212, 0x01016211 }; static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { -- cgit v1.2.2 From 0217f1499cf880d93c64579b2943e9382d8c2c21 Mon Sep 17 00:00:00 2001 From: Andrej Gelenberg Date: Sun, 9 May 2010 22:10:41 +0200 Subject: ALSA: hda - add support for Lenovo ThinkPad X100e in conexant codec Ideapad quirks working for my ThinkPad X100e (microphone is not tested). Signed-off-by: Andrej Gelenberg Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 56e52071c769..d8213e2231a6 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -2846,6 +2846,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), + SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), {} }; -- cgit v1.2.2 From 482c45331519524e4aeaf8a9084a445500822b85 Mon Sep 17 00:00:00 2001 From: Stefan Lippers-Hollmann Date: Mon, 10 May 2010 17:14:34 +0200 Subject: ALSA: Revert "ALSA: hda/realtek: quirk for D945GCLF2 mainboard" This reverts commit 7aee67466536bbf8bb44a95712c848a61c5a0acd. As it doesn't seem to be universally valid for all mainboard revisions of the D945GCLF2 and breaks snd-hda-intel/ snd-hda-codec-realtek on the Intel Corporation "D945GCLF2" (LF94510J.86A.0229.2009.0729.0209) mainboard. 00:1b.0 Audio device [0403]: Intel Corporation N10/ICH 7 Family High Definition Audio Controller [8086:27d8] (rev 01) Signed-off-by: Stefan Lippers-Hollmann Cc: [2.6.33] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7404dba16f83..886d8e46bb37 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -17871,7 +17871,6 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x", ALC663_ASUS_H13), - SND_PCI_QUIRK(0x8086, 0xd604, "Intel mobo", ALC662_3ST_2ch_DIG), {} }; -- cgit v1.2.2 From 0ebf9e3692d640917fb792a7494d05e1f5b1058f Mon Sep 17 00:00:00 2001 From: Daniel T Chen Date: Mon, 10 May 2010 21:50:04 +0200 Subject: ALSA: hda: Fix 0 dB for Lenovo models using Conexant CX20549 (Venice) Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2010-May/027525.html As reported on the mailing list, we also need to cap to the 0 dB offset for Lenovo models, else the sound will be distorted. Reported-and-Tested-by: Tim Starling Cc: Signed-off-by: Daniel T Chen Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index d8213e2231a6..feabb44c7ca4 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1197,9 +1197,10 @@ static int patch_cxt5045(struct hda_codec *codec) case 0x103c: case 0x1631: case 0x1734: - /* HP, Packard Bell, & Fujitsu-Siemens laptops have really bad - * sound over 0dB on NID 0x17. Fix max PCM level to 0 dB - * (originally it has 0x2b steps with 0dB offset 0x14) + case 0x17aa: + /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have + * really bad sound over 0dB on NID 0x17. Fix max PCM level to + * 0 dB (originally it has 0x2b steps with 0dB offset 0x14) */ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, (0x14 << AC_AMPCAP_OFFSET_SHIFT) | -- cgit v1.2.2 From 26ebe0a28986f4845b2c5bea43ac5cc0b9f27f0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 11 May 2010 08:36:29 +0200 Subject: ALSA: hda - Fix mute-LED GPIO pin for HP dv series Old HP dv series seem to use the GPIO pin 0 for controlling the mute LED although the pin is a large package, where the newer models use GPIO 3 in such a case. For fixing the regression from the previous kernels, set spec->gpio_led statically for these model quirks. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 12825aa03106..eb4ea3df5d84 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4766,6 +4766,9 @@ static void set_hp_led_gpio(struct hda_codec *codec) struct sigmatel_spec *spec = codec->spec; unsigned int gpio; + if (spec->gpio_led) + return; + gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); gpio &= AC_GPIO_IO_COUNT; if (gpio > 3) @@ -5683,11 +5686,13 @@ again: * detection. */ spec->hp_detect = 1; + spec->gpio_led = 0x01; break; case STAC_HP_HDX: spec->num_dmics = 1; spec->num_dmuxes = 1; spec->num_smuxes = 1; + spec->gpio_led = 0x08; break; } -- cgit v1.2.2 From 2a6ce6e5fda4721b35f309acedf4cac61ecbfb04 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 May 2010 10:16:20 +0200 Subject: ALSA: hda - Add hp-dv4 model for IDT 92HD71bx It turned out that HP dv series have inconsistent the mute-LED GPIO mapping among various models. dv4/7 seem to use GPIO 0 while dv 5/6 seem to use GPIO 3. The previous commit 26ebe0a28986f4845b2c5bea43ac5cc0b9f27f0a ALSA: hda - Fix mute-LED GPIO pin for HP dv series breaks dv5/6. This patch adds the new quirk model, hp-dv4, to handle HP dv4/7 separately from HP dv5/6. Tested-by: Kunal Gangakhedkar (for dv6-1110ax) Acked-by: Kunal Gangakhedkar Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index eb4ea3df5d84..a0e06d82da1f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -104,6 +104,7 @@ enum { STAC_DELL_M4_2, STAC_DELL_M4_3, STAC_HP_M4, + STAC_HP_DV4, STAC_HP_DV5, STAC_HP_HDX, STAC_HP_DV4_1222NR, @@ -1691,6 +1692,7 @@ static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_DELL_M4_2] = dell_m4_2_pin_configs, [STAC_DELL_M4_3] = dell_m4_3_pin_configs, [STAC_HP_M4] = NULL, + [STAC_HP_DV4] = NULL, [STAC_HP_DV5] = NULL, [STAC_HP_HDX] = NULL, [STAC_HP_DV4_1222NR] = NULL, @@ -1703,6 +1705,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_DELL_M4_2] = "dell-m4-2", [STAC_DELL_M4_3] = "dell-m4-3", [STAC_HP_M4] = "hp-m4", + [STAC_HP_DV4] = "hp-dv4", [STAC_HP_DV5] = "hp-dv5", [STAC_HP_HDX] = "hp-hdx", [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", @@ -1721,7 +1724,7 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080, "HP", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0, - "HP dv4-7", STAC_HP_DV5), + "HP dv4-7", STAC_HP_DV4), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600, "HP dv4-7", STAC_HP_DV5), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610, @@ -5678,6 +5681,9 @@ again: spec->num_smuxes = 1; spec->num_dmuxes = 1; /* fallthrough */ + case STAC_HP_DV4: + spec->gpio_led = 0x01; + /* fallthrough */ case STAC_HP_DV5: snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); @@ -5686,7 +5692,6 @@ again: * detection. */ spec->hp_detect = 1; - spec->gpio_led = 0x01; break; case STAC_HP_HDX: spec->num_dmics = 1; @@ -5749,7 +5754,8 @@ again: } /* enable bass on HP dv7 */ - if (spec->board_config == STAC_HP_DV5) { + if (spec->board_config == STAC_HP_DV4 || + spec->board_config == STAC_HP_DV5) { unsigned int cap; cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); cap &= AC_GPIO_IO_COUNT; -- cgit v1.2.2 From 6a45f7822544c54a2cf070d84f4e85f2fb32ec02 Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Tue, 11 May 2010 16:34:39 +0200 Subject: ALSA: virtuoso: fix Xonar D1/DX front panel microphone Commit 65c3ac885ce9852852b895a4a62212f62cb5f2e9 in 2.6.33 accidentally left out the initialization of the AC97 codec FMIC2MIC bit, which broke recording from the front panel microphone. Signed-off-by: Clemens Ladisch Cc: Signed-off-by: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/pci/oxygen/xonar_cs43xx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c index 16c226bfcd2b..7c4986b27f2b 100644 --- a/sound/pci/oxygen/xonar_cs43xx.c +++ b/sound/pci/oxygen/xonar_cs43xx.c @@ -56,6 +56,7 @@ #include #include #include "xonar.h" +#include "cm9780.h" #include "cs4398.h" #include "cs4362a.h" @@ -172,6 +173,8 @@ static void xonar_d1_init(struct oxygen *chip) oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE); + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + xonar_init_cs53x1(chip); xonar_enable_output(chip); -- cgit v1.2.2 From 8213466596bf10b75887754773ee13c10cf86f5c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 May 2010 16:43:32 +0200 Subject: ALSA: ice1724 - Fix ESI Maya44 capture source control The capture source control of maya44 was wrongly coded with the bit shift instead of the bit mask. Also, the slot for line-in was wrongly assigned (slot 5 instead of 4). Reported-by: Alex Chernyshoff Cc: Signed-off-by: Takashi Iwai --- sound/pci/ice1712/maya44.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c index 3e1c20ae2f1c..726fd4b92e19 100644 --- a/sound/pci/ice1712/maya44.c +++ b/sound/pci/ice1712/maya44.c @@ -347,7 +347,7 @@ static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol, /* known working input slots (0-4) */ #define MAYA_LINE_IN 1 /* in-2 */ -#define MAYA_MIC_IN 4 /* in-5 */ +#define MAYA_MIC_IN 3 /* in-4 */ static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line) { @@ -393,8 +393,8 @@ static int maya_rec_src_put(struct snd_kcontrol *kcontrol, int changed; mutex_lock(&chip->mutex); - changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY, - sel ? GPIO_MIC_RELAY : 0); + changed = maya_set_gpio_bits(chip->ice, 1 << GPIO_MIC_RELAY, + sel ? (1 << GPIO_MIC_RELAY) : 0); wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN); mutex_unlock(&chip->mutex); return changed; -- cgit v1.2.2