diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/Kconfig | 188 | ||||
-rw-r--r-- | sound/pci/hda/Makefile | 75 | ||||
-rw-r--r-- | sound/pci/hda/hda_beep.c | 10 | ||||
-rw-r--r-- | sound/pci/hda/hda_beep.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 786 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 107 | ||||
-rw-r--r-- | sound/pci/hda/hda_eld.c | 590 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 21 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 234 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 379 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 100 | ||||
-rw-r--r-- | sound/pci/hda/hda_patch.h | 22 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 93 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 135 | ||||
-rw-r--r-- | sound/pci/hda/patch_atihdmi.c | 40 | ||||
-rw-r--r-- | sound/pci/hda/patch_cmedia.c | 27 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 39 | ||||
-rw-r--r-- | sound/pci/hda/patch_intelhdmi.c | 711 | ||||
-rw-r--r-- | sound/pci/hda/patch_nvhdmi.c | 33 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 1880 | ||||
-rw-r--r-- | sound/pci/hda/patch_si3054.c | 35 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 1659 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 171 |
23 files changed, 5309 insertions, 2027 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig new file mode 100644 index 000000000000..eb2a19b894a0 --- /dev/null +++ b/sound/pci/hda/Kconfig @@ -0,0 +1,188 @@ +menuconfig SND_HDA_INTEL + tristate "Intel HD Audio" + select SND_PCM + select SND_VMASTER + select SND_JACK if INPUT=y || INPUT=SND + help + Say Y here to include support for Intel "High Definition + Audio" (Azalia) and its compatible devices. + + This option enables the HD-audio controller. Don't forget + to choose the appropriate codec options below. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-intel. + +if SND_HDA_INTEL + +config SND_HDA_HWDEP + bool "Build hwdep interface for HD-audio driver" + select SND_HWDEP + help + Say Y here to build a hwdep interface for HD-audio driver. + This interface can be used for out-of-band communication + with codecs for debugging purposes. + +config SND_HDA_RECONFIG + bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)" + depends on SND_HDA_HWDEP && EXPERIMENTAL + help + Say Y here to enable the HD-audio codec re-configuration feature. + This adds the sysfs interfaces to allow user to clear the whole + codec configuration, change the codec setup, add extra verbs, + and re-configure the codec dynamically. + +config SND_HDA_INPUT_BEEP + bool "Support digital beep via input layer" + depends on INPUT=y || INPUT=SND_HDA_INTEL + help + Say Y here to build a digital beep interface for HD-audio + driver. This interface is used to generate digital beeps. + +config SND_HDA_CODEC_REALTEK + bool "Build Realtek HD-audio codec support" + default y + help + Say Y here to include Realtek HD-audio codec support in + snd-hda-intel driver, such as ALC880. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-realtek. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_ANALOG + bool "Build Analog Device HD-audio codec support" + default y + help + Say Y here to include Analog Device HD-audio codec support in + snd-hda-intel driver, such as AD1986A. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-analog. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_SIGMATEL + bool "Build IDT/Sigmatel HD-audio codec support" + default y + help + Say Y here to include IDT (Sigmatel) HD-audio codec support in + snd-hda-intel driver, such as STAC9200. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-idt. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_VIA + bool "Build VIA HD-audio codec support" + default y + help + Say Y here to include VIA HD-audio codec support in + snd-hda-intel driver, such as VT1708. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-via. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_ATIHDMI + bool "Build ATI HDMI HD-audio codec support" + default y + help + Say Y here to include ATI HDMI HD-audio codec support in + snd-hda-intel driver, such as ATI RS600 HDMI. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-atihdmi. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_NVHDMI + bool "Build NVIDIA HDMI HD-audio codec support" + default y + help + Say Y here to include NVIDIA HDMI HD-audio codec support in + snd-hda-intel driver, such as NVIDIA MCP78 HDMI. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-nvhdmi. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_INTELHDMI + bool "Build INTEL HDMI HD-audio codec support" + default y + help + Say Y here to include INTEL HDMI HD-audio codec support in + snd-hda-intel driver, such as Eaglelake integrated HDMI. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-intelhdmi. + This module is automatically loaded at probing. + +config SND_HDA_ELD + def_bool y + depends on SND_HDA_CODEC_INTELHDMI + +config SND_HDA_CODEC_CONEXANT + bool "Build Conexant HD-audio codec support" + default y + help + Say Y here to include Conexant HD-audio codec support in + snd-hda-intel driver, such as CX20549. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-conexant. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_CMEDIA + bool "Build C-Media HD-audio codec support" + default y + help + Say Y here to include C-Media HD-audio codec support in + snd-hda-intel driver, such as CMI9880. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-cmedia. + This module is automatically loaded at probing. + +config SND_HDA_CODEC_SI3054 + bool "Build Silicon Labs 3054 HD-modem codec support" + default y + help + Say Y here to include Silicon Labs 3054 HD-modem codec + (and compatibles) support in snd-hda-intel driver. + + When the HD-audio driver is built as a module, the codec + support code is also built as another module, + snd-hda-codec-si3054. + This module is automatically loaded at probing. + +config SND_HDA_GENERIC + bool "Enable generic HD-audio codec parser" + default y + help + Say Y here to enable the generic HD-audio codec parser + in snd-hda-intel driver. + +config SND_HDA_POWER_SAVE + bool "Aggressive power-saving on HD-audio" + help + Say Y here to enable more aggressive power-saving mode on + HD-audio driver. The power-saving timeout can be configured + via power_save option or over sysfs on-the-fly. + +config SND_HDA_POWER_SAVE_DEFAULT + int "Default time-out for HD-audio power-save mode" + depends on SND_HDA_POWER_SAVE + default 0 + help + The default time-out value in seconds for HD-audio automatic + power-save mode. 0 means to disable the power-save mode. + +endif diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 1980c6d207e7..50f9d0967251 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,20 +1,59 @@ -snd-hda-intel-y := hda_intel.o -# since snd-hda-intel is the only driver using hda-codec, -# merge it into a single module although it was originally -# designed to be individual modules -snd-hda-intel-y += hda_codec.o -snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o -snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o -snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o -snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o -snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o +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_HWDEP) += hda_hwdep.o +snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o + +snd-hda-codec-realtek-objs := patch_realtek.o +snd-hda-codec-cmedia-objs := patch_cmedia.o +snd-hda-codec-analog-objs := patch_analog.o +snd-hda-codec-idt-objs := patch_sigmatel.o +snd-hda-codec-si3054-objs := patch_si3054.o +snd-hda-codec-atihdmi-objs := patch_atihdmi.o +snd-hda-codec-conexant-objs := patch_conexant.o +snd-hda-codec-via-objs := patch_via.o +snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o +snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o + +# common driver +obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o + +# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans) +ifdef CONFIG_SND_HDA_CODEC_REALTEK +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o +endif +ifdef CONFIG_SND_HDA_CODEC_CMEDIA +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o +endif +ifdef CONFIG_SND_HDA_CODEC_ANALOG +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o +endif +ifdef CONFIG_SND_HDA_CODEC_SIGMATEL +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o +endif +ifdef CONFIG_SND_HDA_CODEC_SI3054 +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o +endif +ifdef CONFIG_SND_HDA_CODEC_ATIHDMI +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o +endif +ifdef CONFIG_SND_HDA_CODEC_CONEXANT +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o +endif +ifdef CONFIG_SND_HDA_CODEC_VIA +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o +endif +ifdef CONFIG_SND_HDA_CODEC_NVHDMI +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o +endif +ifdef CONFIG_SND_HDA_CODEC_INTELHDMI +obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o +endif + +# this must be the last entry after codec drivers; +# otherwise the codec patches won't be hooked before the PCI probe +# when built in kernel obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 9b77b3e0fa98..e00421c0d8ba 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -37,6 +37,9 @@ static void snd_hda_generate_beep(struct work_struct *work) container_of(work, struct hda_beep, beep_work); struct hda_codec *codec = beep->codec; + if (!beep->enabled) + return; + /* generate tone */ snd_hda_codec_write_cache(codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, beep->tone); @@ -85,6 +88,10 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) 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); + return -ENOMEM; + } /* setup digital beep device */ input_dev->name = "HDA Digital PCBeep"; @@ -115,11 +122,13 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) beep->nid = nid; beep->dev = input_dev; beep->codec = codec; + beep->enabled = 1; codec->beep = beep; INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device); void snd_hda_detach_beep_device(struct hda_codec *codec) { @@ -132,3 +141,4 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) 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 de4036e6e710..b9679f081cae 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -31,6 +31,7 @@ struct hda_beep { char phys[32]; int tone; int nid; + int enabled; struct work_struct beep_work; /* scheduled task for beep event */ }; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 6447754ae56e..e16cf63821ae 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -31,15 +31,6 @@ #include <sound/initval.h> #include "hda_local.h" #include <sound/hda_hwdep.h> -#include "hda_patch.h" /* codec presets */ - -#ifdef CONFIG_SND_HDA_POWER_SAVE -/* define this option here to hide as static */ -static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -module_param(power_save, int, 0644); -MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " - "(in second, 0 = disable)."); -#endif /* * vendor / preset table @@ -55,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = { { 0x1002, "ATI" }, { 0x1057, "Motorola" }, { 0x1095, "Silicon Image" }, + { 0x10de, "Nvidia" }, { 0x10ec, "Realtek" }, { 0x1106, "VIA" }, { 0x111d, "IDT" }, @@ -64,41 +56,33 @@ static struct hda_vendor_id hda_vendor_ids[] = { { 0x14f1, "Conexant" }, { 0x17e8, "Chrontel" }, { 0x1854, "LG" }, + { 0x1aec, "Wolfson Microelectronics" }, { 0x434d, "C-Media" }, + { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, {} /* terminator */ }; -static const struct hda_codec_preset *hda_preset_tables[] = { -#ifdef CONFIG_SND_HDA_CODEC_REALTEK - snd_hda_preset_realtek, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CMEDIA - snd_hda_preset_cmedia, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ANALOG - snd_hda_preset_analog, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL - snd_hda_preset_sigmatel, -#endif -#ifdef CONFIG_SND_HDA_CODEC_SI3054 - snd_hda_preset_si3054, -#endif -#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI - snd_hda_preset_atihdmi, -#endif -#ifdef CONFIG_SND_HDA_CODEC_CONEXANT - snd_hda_preset_conexant, -#endif -#ifdef CONFIG_SND_HDA_CODEC_VIA - snd_hda_preset_via, -#endif -#ifdef CONFIG_SND_HDA_CODEC_NVHDMI - snd_hda_preset_nvhdmi, -#endif - NULL -}; +static DEFINE_MUTEX(preset_mutex); +static LIST_HEAD(hda_preset_tables); + +int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) +{ + mutex_lock(&preset_mutex); + list_add_tail(&preset->list, &hda_preset_tables); + mutex_unlock(&preset_mutex); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset); + +int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) +{ + mutex_lock(&preset_mutex); + list_del(&preset->list); + mutex_unlock(&preset_mutex); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); @@ -107,6 +91,72 @@ static void hda_keep_power_on(struct hda_codec *codec); static inline void hda_keep_power_on(struct hda_codec *codec) {} #endif +const char *snd_hda_get_jack_location(u32 cfg) +{ + static char *bases[7] = { + "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", + }; + static unsigned char specials_idx[] = { + 0x07, 0x08, + 0x17, 0x18, 0x19, + 0x37, 0x38 + }; + static char *specials[] = { + "Rear Panel", "Drive Bar", + "Riser", "HDMI", "ATAPI", + "Mobile-In", "Mobile-Out" + }; + int i; + cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; + if ((cfg & 0x0f) < 7) + return bases[cfg & 0x0f]; + for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { + if (cfg == specials_idx[i]) + return specials[i]; + } + return "UNKNOWN"; +} +EXPORT_SYMBOL_HDA(snd_hda_get_jack_location); + +const char *snd_hda_get_jack_connectivity(u32 cfg) +{ + static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; + + return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3]; +} +EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity); + +const char *snd_hda_get_jack_type(u32 cfg) +{ + static char *jack_types[16] = { + "Line Out", "Speaker", "HP Out", "CD", + "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", + "Line In", "Aux", "Mic", "Telephony", + "SPDIF In", "Digitial In", "Reserved", "Other" + }; + + return jack_types[(cfg & AC_DEFCFG_DEVICE) + >> AC_DEFCFG_DEVICE_SHIFT]; +} +EXPORT_SYMBOL_HDA(snd_hda_get_jack_type); + +/* + * Compose a 32bit command word to be sent to the HD-audio controller + */ +static inline unsigned int +make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, + unsigned int verb, unsigned int parm) +{ + u32 val; + + val = (u32)(codec->addr & 0x0f) << 28; + val |= (u32)direct << 27; + val |= (u32)nid << 20; + val |= verb << 8; + val |= parm; + return val; +} + /** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec @@ -123,17 +173,21 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { + struct hda_bus *bus = codec->bus; unsigned int res; + + res = make_codec_cmd(codec, nid, direct, verb, parm); snd_hda_power_up(codec); - mutex_lock(&codec->bus->cmd_mutex); - if (!codec->bus->ops.command(codec, nid, direct, verb, parm)) - res = codec->bus->ops.get_response(codec); + mutex_lock(&bus->cmd_mutex); + if (!bus->ops.command(bus, res)) + res = bus->ops.get_response(bus); else res = (unsigned int)-1; - mutex_unlock(&codec->bus->cmd_mutex); + mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); return res; } +EXPORT_SYMBOL_HDA(snd_hda_codec_read); /** * snd_hda_codec_write - send a single command without waiting for response @@ -150,14 +204,19 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { + struct hda_bus *bus = codec->bus; + unsigned int res; int err; + + res = make_codec_cmd(codec, nid, direct, verb, parm); snd_hda_power_up(codec); - mutex_lock(&codec->bus->cmd_mutex); - err = codec->bus->ops.command(codec, nid, direct, verb, parm); - mutex_unlock(&codec->bus->cmd_mutex); + mutex_lock(&bus->cmd_mutex); + err = bus->ops.command(bus, res); + mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); return err; } +EXPORT_SYMBOL_HDA(snd_hda_codec_write); /** * snd_hda_sequence_write - sequence writes @@ -172,6 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) for (; seq->nid; seq++) snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); } +EXPORT_SYMBOL_HDA(snd_hda_sequence_write); /** * snd_hda_get_sub_nodes - get the range of sub nodes @@ -193,6 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, *start_id = (parm >> 16) & 0x7fff; return (int)(parm & 0x7fff); } +EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); /** * snd_hda_get_connections - get connection list @@ -281,6 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } return conns; } +EXPORT_SYMBOL_HDA(snd_hda_get_connections); /** @@ -315,6 +377,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) return 0; } +EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event); /* * process queued unsolicited events @@ -344,7 +407,7 @@ static void process_unsol_events(struct work_struct *work) /* * initialize unsolicited queue */ -static int __devinit init_unsol_queue(struct hda_bus *bus) +static int init_unsol_queue(struct hda_bus *bus) { struct hda_bus_unsolicited *unsol; @@ -390,9 +453,24 @@ static int snd_hda_bus_free(struct hda_bus *bus) static int snd_hda_bus_dev_free(struct snd_device *device) { struct hda_bus *bus = device->device_data; + bus->shutdown = 1; return snd_hda_bus_free(bus); } +#ifdef CONFIG_SND_HDA_HWDEP +static int snd_hda_bus_dev_register(struct snd_device *device) +{ + struct hda_bus *bus = device->device_data; + struct hda_codec *codec; + list_for_each_entry(codec, &bus->codec_list, list) { + snd_hda_hwdep_add_sysfs(codec); + } + return 0; +} +#else +#define snd_hda_bus_dev_register NULL +#endif + /** * snd_hda_bus_new - create a HDA bus * @card: the card entry @@ -401,13 +479,14 @@ static int snd_hda_bus_dev_free(struct snd_device *device) * * Returns 0 if successful, or a negative error code. */ -int __devinit snd_hda_bus_new(struct snd_card *card, +int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp) { struct hda_bus *bus; int err; static struct snd_device_ops dev_ops = { + .dev_register = snd_hda_bus_dev_register, .dev_free = snd_hda_bus_dev_free, }; @@ -429,6 +508,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card, bus->private_data = temp->private_data; bus->pci = temp->pci; bus->modelname = temp->modelname; + bus->power_save = temp->power_save; bus->ops = temp->ops; mutex_init(&bus->cmd_mutex); @@ -443,27 +523,42 @@ int __devinit snd_hda_bus_new(struct snd_card *card, *busp = bus; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_bus_new); #ifdef CONFIG_SND_HDA_GENERIC #define is_generic_config(codec) \ - (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic")) + (codec->modelname && !strcmp(codec->modelname, "generic")) #else #define is_generic_config(codec) 0 #endif +#ifdef MODULE +#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */ +#else +#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */ +#endif + /* * find a matching codec preset */ -static const struct hda_codec_preset __devinit * +static const struct hda_codec_preset * find_codec_preset(struct hda_codec *codec) { - const struct hda_codec_preset **tbl, *preset; + struct hda_codec_preset_list *tbl; + const struct hda_codec_preset *preset; + int mod_requested = 0; if (is_generic_config(codec)) return NULL; /* use the generic parser */ - for (tbl = hda_preset_tables; *tbl; tbl++) { - for (preset = *tbl; preset->id; preset++) { + again: + mutex_lock(&preset_mutex); + list_for_each_entry(tbl, &hda_preset_tables, list) { + if (!try_module_get(tbl->owner)) { + snd_printk(KERN_ERR "hda_codec: cannot module_get\n"); + continue; + } + for (preset = tbl->preset; preset->id; preset++) { u32 mask = preset->mask; if (preset->afg && preset->afg != codec->afg) continue; @@ -473,23 +568,40 @@ find_codec_preset(struct hda_codec *codec) mask = ~0; if (preset->id == (codec->vendor_id & mask) && (!preset->rev || - preset->rev == codec->revision_id)) + preset->rev == codec->revision_id)) { + mutex_unlock(&preset_mutex); + codec->owner = tbl->owner; return preset; + } } + module_put(tbl->owner); + } + mutex_unlock(&preset_mutex); + + if (mod_requested < HDA_MODREQ_MAX_COUNT) { + char name[32]; + if (!mod_requested) + snprintf(name, sizeof(name), "snd-hda-codec-id:%08x", + codec->vendor_id); + else + snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*", + (codec->vendor_id >> 16) & 0xffff); + request_module(name); + mod_requested++; + goto again; } return NULL; } /* - * snd_hda_get_codec_name - store the codec name + * get_codec_name - store the codec name */ -void snd_hda_get_codec_name(struct hda_codec *codec, - char *name, int namelen) +static int get_codec_name(struct hda_codec *codec) { const struct hda_vendor_id *c; const char *vendor = NULL; u16 vendor_id = codec->vendor_id >> 16; - char tmp[16]; + char tmp[16], name[32]; for (c = hda_vendor_ids; c->id; c++) { if (c->id == vendor_id) { @@ -502,16 +614,21 @@ void snd_hda_get_codec_name(struct hda_codec *codec, vendor = tmp; } if (codec->preset && codec->preset->name) - snprintf(name, namelen, "%s %s", vendor, codec->preset->name); + snprintf(name, sizeof(name), "%s %s", vendor, + codec->preset->name); else - snprintf(name, namelen, "%s ID %x", vendor, + snprintf(name, sizeof(name), "%s ID %x", vendor, codec->vendor_id & 0xffff); + codec->name = kstrdup(name, GFP_KERNEL); + if (!codec->name) + return -ENOMEM; + return 0; } /* * look for an AFG and MFG nodes */ -static void __devinit setup_fg_nodes(struct hda_codec *codec) +static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec) { int i, total_nodes; hda_nid_t nid; @@ -570,11 +687,15 @@ static void snd_hda_codec_free(struct hda_codec *codec) flush_scheduled_work(); #endif list_del(&codec->list); + snd_array_free(&codec->mixers); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); + module_put(codec->owner); free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); + kfree(codec->name); + kfree(codec->modelname); kfree(codec->wcaps); kfree(codec); } @@ -587,8 +708,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) * * 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, + int do_init, struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -616,6 +737,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, mutex_init(&codec->spdif_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); + if (codec->bus->modelname) { + codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); + if (!codec->modelname) { + snd_hda_codec_free(codec); + return -ENODEV; + } + } #ifdef CONFIG_SND_HDA_POWER_SAVE INIT_DELAYED_WORK(&codec->power_work, hda_power_work); @@ -661,12 +790,44 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_SUBSYSTEM_ID, 0); } + if (bus->modelname) + codec->modelname = kstrdup(bus->modelname, GFP_KERNEL); + + if (do_init) { + err = snd_hda_codec_configure(codec); + if (err < 0) { + snd_hda_codec_free(codec); + return err; + } + } + snd_hda_codec_proc_new(codec); + + snd_hda_create_hwdep(codec); + + sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, + codec->subsystem_id, codec->revision_id); + snd_component_add(codec->bus->card, component); + + if (codecp) + *codecp = codec; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_new); + +int snd_hda_codec_configure(struct hda_codec *codec) +{ + int err; codec->preset = find_codec_preset(codec); + if (!codec->name) { + err = get_codec_name(codec); + if (err < 0) + return err; + } /* audio codec should override the mixer name */ - if (codec->afg || !*bus->card->mixername) - snd_hda_get_codec_name(codec, bus->card->mixername, - sizeof(bus->card->mixername)); + if (codec->afg || !*codec->bus->card->mixername) + strlcpy(codec->bus->card->mixername, codec->name, + sizeof(codec->bus->card->mixername)); if (is_generic_config(codec)) { err = snd_hda_parse_generic_codec(codec); @@ -683,25 +844,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, printk(KERN_ERR "hda-codec: No codec parser is available\n"); patched: - if (err < 0) { - snd_hda_codec_free(codec); - return err; - } - - if (codec->patch_ops.unsol_event) - init_unsol_queue(bus); - - snd_hda_codec_proc_new(codec); -#ifdef CONFIG_SND_HDA_HWDEP - snd_hda_create_hwdep(codec); -#endif - - sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id); - snd_component_add(codec->bus->card, component); - - if (codecp) - *codecp = codec; - return 0; + if (!err && codec->patch_ops.unsol_event) + err = init_unsol_queue(codec->bus); + return err; } /** @@ -727,6 +872,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, msleep(1); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } +EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream); void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) { @@ -740,6 +886,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0); #endif } +EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream); /* * amp access functions @@ -751,17 +898,17 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid) #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) /* initialize the hash table */ -static void __devinit init_hda_cache(struct hda_cache_rec *cache, +static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache, unsigned int record_size) { memset(cache, 0, sizeof(*cache)); memset(cache->hash, 0xff, sizeof(cache->hash)); - cache->record_size = record_size; + snd_array_init(&cache->buf, record_size, 64); } static void free_hda_cache(struct hda_cache_rec *cache) { - kfree(cache->buffer); + snd_array_free(&cache->buf); } /* query the hash. allocate an entry if not found. */ @@ -773,35 +920,17 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, struct hda_cache_head *info; while (cur != 0xffff) { - info = (struct hda_cache_head *)(cache->buffer + - cur * cache->record_size); + info = snd_array_elem(&cache->buf, cur); if (info->key == key) return info; cur = info->next; } /* add a new hash entry */ - if (cache->num_entries >= cache->size) { - /* reallocate the array */ - unsigned int new_size = cache->size + 64; - void *new_buffer; - new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL); - if (!new_buffer) { - snd_printk(KERN_ERR "hda_codec: " - "can't malloc amp_info\n"); - return NULL; - } - if (cache->buffer) { - memcpy(new_buffer, cache->buffer, - cache->size * cache->record_size); - kfree(cache->buffer); - } - cache->size = new_size; - cache->buffer = new_buffer; - } - cur = cache->num_entries++; - info = (struct hda_cache_head *)(cache->buffer + - cur * cache->record_size); + info = snd_array_new(&cache->buf); + if (!info) + return NULL; + cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; info->next = cache->hash[idx]; @@ -839,6 +968,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) } return info->amp_caps; } +EXPORT_SYMBOL_HDA(query_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) @@ -852,6 +982,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, info->head.val |= INFO_AMP_CAPS; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); /* * read the current volume to info @@ -905,6 +1036,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; return get_vol_mute(codec, info, nid, ch, direction, index); } +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); /* * update the AMP value, mask = bit mask to set, val = the value @@ -924,6 +1056,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); /* * update the AMP stereo with the same mask and value @@ -937,15 +1070,16 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, idx, mask, val); return ret; } +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); #ifdef SND_HDA_NEEDS_RESUME /* 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.buffer; + struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i; - for (i = 0; i < codec->amp_cache.size; i++, buffer++) { + for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { u32 key = buffer->head.key; hda_nid_t nid; unsigned int idx, dir, ch; @@ -962,6 +1096,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) } } } +EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); #endif /* SND_HDA_NEEDS_RESUME */ /* volume */ @@ -989,6 +1124,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = caps; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info); int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1008,6 +1144,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, & HDA_AMP_VOLMASK; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get); int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1032,6 +1169,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, snd_hda_power_down(codec); return change; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put); int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv) @@ -1058,6 +1196,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv); /* * set (static) TLV for virtual master volume; recalculated as max 0dB @@ -1077,6 +1216,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, tlv[2] = -nums * step; tlv[3] = step; } +EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv); /* find a mixer control element with the given name */ static struct snd_kcontrol * @@ -1096,6 +1236,69 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, { return _snd_hda_find_mixer_ctl(codec, name, 0); } +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 err; + struct snd_kcontrol **knewp; + + err = snd_ctl_add(codec->bus->card, kctl); + if (err < 0) + return err; + knewp = snd_array_new(&codec->mixers); + if (!knewp) + return -ENOMEM; + *knewp = kctl; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_ctl_add); + +#ifdef CONFIG_SND_HDA_RECONFIG +/* Clear all controls assigned to the given codec */ +void snd_hda_ctls_clear(struct hda_codec *codec) +{ + int i; + struct snd_kcontrol **kctls = codec->mixers.list; + for (i = 0; i < codec->mixers.used; i++) + snd_ctl_remove(codec->bus->card, kctls[i]); + snd_array_free(&codec->mixers); +} + +void snd_hda_codec_reset(struct hda_codec *codec) +{ + int i; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + cancel_delayed_work(&codec->power_work); + flush_scheduled_work(); +#endif + snd_hda_ctls_clear(codec); + /* relase PCMs */ + for (i = 0; i < codec->num_pcms; i++) { + if (codec->pcm_info[i].pcm) { + snd_device_free(codec->bus->card, + codec->pcm_info[i].pcm); + clear_bit(codec->pcm_info[i].device, + codec->bus->pcm_dev_bits); + } + } + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + codec->proc_widget_hook = NULL; + codec->spec = NULL; + free_hda_cache(&codec->amp_cache); + free_hda_cache(&codec->cmd_cache); + init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); + init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); + codec->num_pcms = 0; + codec->pcm_info = NULL; + codec->preset = NULL; + module_put(codec->owner); + codec->owner = NULL; +} +#endif /* CONFIG_SND_HDA_RECONFIG */ /* create a virtual master control and add slaves */ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, @@ -1114,7 +1317,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_ctl_add(codec->bus->card, kctl); + err = snd_hda_ctl_add(codec, kctl); if (err < 0) return err; @@ -1132,6 +1335,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name, } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_add_vmaster); /* switch */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, @@ -1145,6 +1349,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 1; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info); int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1164,6 +1369,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, HDA_AMP_MUTE) ? 0 : 1; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1194,6 +1400,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, snd_hda_power_down(codec); return change; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put); /* * bound volume controls @@ -1219,6 +1426,7 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->spdif_mutex); return err; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get); int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1242,6 +1450,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->spdif_mutex); return err < 0 ? err : change; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put); /* * generic bound volume/swtich controls @@ -1261,6 +1470,7 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->spdif_mutex); return err; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info); int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1277,6 +1487,7 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->spdif_mutex); return err; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get); int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1299,6 +1510,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, mutex_unlock(&codec->spdif_mutex); return err < 0 ? err : change; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put); int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) @@ -1315,6 +1527,7 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, mutex_unlock(&codec->spdif_mutex); return err; } +EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv); struct hda_ctl_ops snd_hda_bind_vol = { .info = snd_hda_mixer_amp_volume_info, @@ -1322,6 +1535,7 @@ struct hda_ctl_ops snd_hda_bind_vol = { .put = snd_hda_mixer_amp_volume_put, .tlv = snd_hda_mixer_amp_tlv }; +EXPORT_SYMBOL_HDA(snd_hda_bind_vol); struct hda_ctl_ops snd_hda_bind_sw = { .info = snd_hda_mixer_amp_switch_info, @@ -1329,6 +1543,7 @@ struct hda_ctl_ops snd_hda_bind_sw = { .put = snd_hda_mixer_amp_switch_put, .tlv = snd_hda_mixer_amp_tlv }; +EXPORT_SYMBOL_HDA(snd_hda_bind_sw); /* * SPDIF out controls @@ -1435,12 +1650,12 @@ static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, { hda_nid_t *d; - snd_hda_codec_write(codec, nid, 0, verb, val); + snd_hda_codec_write_cache(codec, nid, 0, verb, val); d = codec->slave_dig_outs; if (!d) return; for (; *d; d++) - snd_hda_codec_write(codec, *d, 0, verb, val); + snd_hda_codec_write_cache(codec, *d, 0, verb, val); } static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, @@ -1576,9 +1791,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) } for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); + if (!kctl) + return -ENOMEM; kctl->id.index = idx; kctl->private_value = nid; - err = snd_ctl_add(codec->bus->card, kctl); + err = snd_hda_ctl_add(codec, kctl); if (err < 0) return err; } @@ -1588,6 +1805,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); /* * SPDIF sharing with analog output @@ -1622,9 +1840,10 @@ 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_ctl_add(codec->bus->card, + return snd_hda_ctl_add(codec, snd_ctl_new1(&spdif_share_sw, mout)); } +EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw); /* * SPDIF input @@ -1724,7 +1943,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) { kctl = snd_ctl_new1(dig_mix, codec); kctl->private_value = nid; - err = snd_ctl_add(codec->bus->card, kctl); + err = snd_hda_ctl_add(codec, kctl); if (err < 0) return err; } @@ -1734,6 +1953,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) AC_DIG1_ENABLE; return 0; } +EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); #ifdef SND_HDA_NEEDS_RESUME /* @@ -1760,10 +1980,14 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { + struct hda_bus *bus = codec->bus; + unsigned int res; int err; + + res = make_codec_cmd(codec, nid, direct, verb, parm); snd_hda_power_up(codec); - mutex_lock(&codec->bus->cmd_mutex); - err = codec->bus->ops.command(codec, nid, direct, verb, parm); + mutex_lock(&bus->cmd_mutex); + err = bus->ops.command(bus, res); if (!err) { struct hda_cache_head *c; u32 key = build_cmd_cache_key(nid, verb); @@ -1771,18 +1995,19 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, if (c) c->val = parm; } - mutex_unlock(&codec->bus->cmd_mutex); + mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); return err; } +EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); /* resume the all commands from the cache */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { - struct hda_cache_head *buffer = codec->cmd_cache.buffer; + struct hda_cache_head *buffer = codec->cmd_cache.buf.list; int i; - for (i = 0; i < codec->cmd_cache.size; i++, buffer++) { + for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { u32 key = buffer->key; if (!key) continue; @@ -1790,6 +2015,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec) get_cmd_cache_cmd(key), buffer->val); } } +EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache); /** * snd_hda_sequence_write_cache - sequence writes with caching @@ -1807,6 +2033,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, seq->param); } +EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); #endif /* SND_HDA_NEEDS_RESUME */ /* @@ -1867,6 +2094,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, } } +#ifdef CONFIG_SND_HDA_HWDEP +/* execute additional init verbs */ +static void hda_exec_init_verbs(struct hda_codec *codec) +{ + if (codec->init_verbs.list) + snd_hda_sequence_write(codec, codec->init_verbs.list); +} +#else +static inline void hda_exec_init_verbs(struct hda_codec *codec) {} +#endif + #ifdef SND_HDA_NEEDS_RESUME /* * call suspend and power-down; used both from PM and power-save @@ -1893,6 +2131,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); + hda_exec_init_verbs(codec); if (codec->patch_ops.resume) codec->patch_ops.resume(codec); else { @@ -1913,28 +2152,37 @@ static void hda_call_codec_resume(struct hda_codec *codec) * * Returns 0 if successful, otherwise a negative error code. */ -int __devinit snd_hda_build_controls(struct hda_bus *bus) +int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus) { struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { - int err = 0; - /* fake as if already powered-on */ - hda_keep_power_on(codec); - /* then fire up */ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); - /* continue to initialize... */ - if (codec->patch_ops.init) - err = codec->patch_ops.init(codec); - if (!err && codec->patch_ops.build_controls) - err = codec->patch_ops.build_controls(codec); - snd_hda_power_down(codec); + int err = snd_hda_codec_build_controls(codec); if (err < 0) return err; } + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_build_controls); +int snd_hda_codec_build_controls(struct hda_codec *codec) +{ + int err = 0; + /* fake as if already powered-on */ + hda_keep_power_on(codec); + /* then fire up */ + hda_set_power_state(codec, + codec->afg ? codec->afg : codec->mfg, + AC_PWRST_D0); + hda_exec_init_verbs(codec); + /* continue to initialize... */ + if (codec->patch_ops.init) + err = codec->patch_ops.init(codec); + if (!err && codec->patch_ops.build_controls) + err = codec->patch_ops.build_controls(codec); + snd_hda_power_down(codec); + if (err < 0) + return err; return 0; } @@ -2027,6 +2275,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, return val; } +EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats @@ -2041,7 +2290,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, * * Returns 0 if successful, otherwise a negative error code. */ -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, +static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp) { int i; @@ -2206,6 +2455,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, return 1; } +EXPORT_SYMBOL_HDA(snd_hda_is_supported_format); /* * PCM stuff @@ -2235,8 +2485,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo, return 0; } -static int __devinit set_pcm_default_values(struct hda_codec *codec, - struct hda_pcm_stream *info) +static int set_pcm_default_values(struct hda_codec *codec, + struct hda_pcm_stream *info) { /* query support PCM information from the given NID */ if (info->nid && (!info->rates || !info->formats)) { @@ -2262,6 +2512,110 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec, return 0; } +/* + * 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" + }; + /* starting device index for each PCM type */ + static int dev_idx[HDA_PCM_NTYPES] = { + [HDA_PCM_TYPE_AUDIO] = 0, + [HDA_PCM_TYPE_SPDIF] = 1, + [HDA_PCM_TYPE_HDMI] = 3, + [HDA_PCM_TYPE_MODEM] = 6 + }; + /* normal audio device indices; not linear to keep compatibility */ + static int audio_idx[4] = { 0, 2, 4, 5 }; + 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)) + break; + } + if (i >= ARRAY_SIZE(audio_idx)) { + snd_printk(KERN_WARNING "Too many audio devices\n"); + return -EAGAIN; + } + break; + 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: + snd_printk(KERN_WARNING "Invalid PCM type %d\n", type); + return -EINVAL; + } + set_bit(dev, bus->pcm_dev_bits); + return dev; +} + +/* + * attach a new PCM stream + */ +static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) +{ + struct hda_bus *bus = codec->bus; + struct hda_pcm_stream *info; + int stream, err; + + if (snd_BUG_ON(!pcm->name)) + return -EINVAL; + for (stream = 0; stream < 2; stream++) { + info = &pcm->stream[stream]; + if (info->substreams) { + err = set_pcm_default_values(codec, info); + if (err < 0) + return err; + } + } + return bus->ops.attach_pcm(bus, codec, pcm); +} + +/* assign all PCMs of the given codec */ +int snd_hda_codec_build_pcms(struct hda_codec *codec) +{ + unsigned int pcm; + int err; + + if (!codec->num_pcms) { + if (!codec->patch_ops.build_pcms) + return 0; + err = codec->patch_ops.build_pcms(codec); + if (err < 0) + return err; + } + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + int dev; + + if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) + return 0; /* no substreams assigned */ + + if (!cpcm->pcm) { + dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); + if (dev < 0) + return 0; + cpcm->device = dev; + err = snd_hda_attach_pcm(codec, cpcm); + if (err < 0) + return err; + } + } + return 0; +} + /** * snd_hda_build_pcms - build PCM information * @bus: the BUS @@ -2293,27 +2647,13 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { - unsigned int pcm, s; - int err; - if (!codec->patch_ops.build_pcms) - continue; - err = codec->patch_ops.build_pcms(codec); + int err = snd_hda_codec_build_pcms(codec); if (err < 0) return err; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - for (s = 0; s < 2; s++) { - struct hda_pcm_stream *info; - info = &codec->pcm_info[pcm].stream[s]; - if (!info->substreams) - continue; - err = set_pcm_default_values(codec, info); - if (err < 0) - return err; - } - } } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_build_pcms); /** * snd_hda_check_board_config - compare the current codec with the config table @@ -2332,11 +2672,11 @@ int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, const char **models, const struct snd_pci_quirk *tbl) { - if (codec->bus->modelname && models) { + if (codec->modelname && models) { int i; for (i = 0; i < num_configs; i++) { if (models[i] && - !strcmp(codec->bus->modelname, models[i])) { + !strcmp(codec->modelname, models[i])) { snd_printd(KERN_INFO "hda_codec: model '%s' is " "selected\n", models[i]); return i; @@ -2369,6 +2709,7 @@ int snd_hda_check_board_config(struct hda_codec *codec, } return -1; } +EXPORT_SYMBOL_HDA(snd_hda_check_board_config); /** * snd_hda_add_new_ctls - create controls from the array @@ -2389,7 +2730,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_ctl_add(codec->bus->card, kctl); + err = snd_hda_ctl_add(codec, kctl); if (err < 0) { if (!codec->addr) return err; @@ -2397,13 +2738,14 @@ 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_ctl_add(codec->bus->card, kctl); + err = snd_hda_ctl_add(codec, kctl); if (err < 0) return err; } } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, @@ -2413,6 +2755,7 @@ static void hda_power_work(struct work_struct *work) { struct hda_codec *codec = container_of(work, struct hda_codec, power_work.work); + struct hda_bus *bus = codec->bus; if (!codec->power_on || codec->power_count) { codec->power_transition = 0; @@ -2420,8 +2763,8 @@ static void hda_power_work(struct work_struct *work) } hda_call_codec_suspend(codec); - if (codec->bus->ops.pm_notify) - codec->bus->ops.pm_notify(codec); + if (bus->ops.pm_notify) + bus->ops.pm_notify(bus); } static void hda_keep_power_on(struct hda_codec *codec) @@ -2432,29 +2775,39 @@ static void hda_keep_power_on(struct hda_codec *codec) void snd_hda_power_up(struct hda_codec *codec) { + struct hda_bus *bus = codec->bus; + codec->power_count++; if (codec->power_on || codec->power_transition) return; codec->power_on = 1; - if (codec->bus->ops.pm_notify) - codec->bus->ops.pm_notify(codec); + if (bus->ops.pm_notify) + bus->ops.pm_notify(bus); hda_call_codec_resume(codec); cancel_delayed_work(&codec->power_work); codec->power_transition = 0; } +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) void snd_hda_power_down(struct hda_codec *codec) { --codec->power_count; if (!codec->power_on || codec->power_count || codec->power_transition) return; - if (power_save) { + if (power_save(codec)) { codec->power_transition = 1; /* avoid reentrance */ schedule_delayed_work(&codec->power_work, - msecs_to_jiffies(power_save * 1000)); + msecs_to_jiffies(power_save(codec) * 1000)); } } +EXPORT_SYMBOL_HDA(snd_hda_power_down); int snd_hda_check_amp_list_power(struct hda_codec *codec, struct hda_loopback_check *check, @@ -2491,6 +2844,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power); #endif /* @@ -2510,6 +2864,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec, chmode[uinfo->value.enumerated.item].channels); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info); int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -2527,6 +2882,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec, } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get); int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -2547,6 +2903,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec, snd_hda_sequence_write_cache(codec, chmode[mode].sequence); return 1; } +EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put); /* * input MUX helper @@ -2567,6 +2924,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux, strcpy(uinfo->value.enumerated.name, imux->items[index].label); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_input_mux_info); int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, @@ -2588,6 +2946,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec, *cur_val = idx; return 1; } +EXPORT_SYMBOL_HDA(snd_hda_input_mux_put); /* @@ -2640,6 +2999,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, mutex_unlock(&codec->spdif_mutex); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open); int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -2652,6 +3012,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, mutex_unlock(&codec->spdif_mutex); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare); /* * release the digital out @@ -2664,6 +3025,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec, mutex_unlock(&codec->spdif_mutex); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close); /* * set up more restrictions for analog out @@ -2703,6 +3065,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec, return snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open); /* * set up the i/o for analog out @@ -2761,6 +3124,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); /* * clean up the setting for analog out @@ -2787,6 +3151,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, mutex_unlock(&codec->spdif_mutex); return 0; } +EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); /* * Helper for automatic pin configuration @@ -3072,11 +3437,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, return 0; } +EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config); /* labels for input pins */ const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = { "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" }; +EXPORT_SYMBOL_HDA(auto_pin_cfg_labels); #ifdef CONFIG_PM @@ -3104,11 +3471,11 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state) } return 0; } +EXPORT_SYMBOL_HDA(snd_hda_suspend); /** * snd_hda_resume - resume the codecs * @bus: the HDA bus - * @state: resume state * * Returns 0 if successful. * @@ -3125,16 +3492,79 @@ int snd_hda_resume(struct hda_bus *bus) } return 0; } -#ifdef CONFIG_SND_HDA_POWER_SAVE -int snd_hda_codecs_inuse(struct hda_bus *bus) -{ - struct hda_codec *codec; +EXPORT_SYMBOL_HDA(snd_hda_resume); +#endif /* CONFIG_PM */ - list_for_each_entry(codec, &bus->codec_list, list) { - if (snd_hda_codec_needs_resume(codec)) - return 1; +/* + * generic arrays + */ + +/* get a new element from the given array + * if it exceeds the pre-allocated array size, re-allocate the array + */ +void *snd_array_new(struct snd_array *array) +{ + if (array->used >= array->alloced) { + int num = array->alloced + array->alloc_align; + void *nlist; + if (snd_BUG_ON(num >= 4096)) + return NULL; + nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL); + if (!nlist) + return NULL; + if (array->list) { + memcpy(nlist, array->list, + array->elem_size * array->alloced); + kfree(array->list); + } + array->list = nlist; + array->alloced = num; } - return 0; + return snd_array_elem(array, array->used++); } -#endif -#endif +EXPORT_SYMBOL_HDA(snd_array_new); + +/* free the given array elements */ +void snd_array_free(struct snd_array *array) +{ + kfree(array->list); + array->used = 0; + array->alloced = 0; + array->list = NULL; +} +EXPORT_SYMBOL_HDA(snd_array_free); + +/* + * used by hda_proc.c and hda_eld.c + */ +void snd_print_pcm_rates(int pcm, char *buf, int buflen) +{ + static unsigned int rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, + 96000, 176400, 192000, 384000 + }; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++) + if (pcm & (1 << i)) + j += snprintf(buf + j, buflen - j, " %d", rates[i]); + + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_HDA(snd_print_pcm_rates); + +void snd_print_pcm_bits(int pcm, char *buf, int buflen) +{ + static unsigned int bits[] = { 8, 16, 20, 24, 32 }; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) + if (pcm & (AC_SUPPCM_BITS_8 << i)) + j += snprintf(buf + j, buflen - j, " %d", bits[i]); + + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_HDA(snd_print_pcm_bits); + +MODULE_DESCRIPTION("HDA codec core"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 60468f562400..729fc7642d7f 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -520,6 +520,36 @@ enum { #define HDA_MAX_CODEC_ADDRESS 0x0f /* + * generic arrays + */ +struct snd_array { + unsigned int used; + unsigned int alloced; + unsigned int elem_size; + unsigned int alloc_align; + void *list; +}; + +void *snd_array_new(struct snd_array *array); +void snd_array_free(struct snd_array *array); +static inline void snd_array_init(struct snd_array *array, unsigned int size, + unsigned int align) +{ + array->elem_size = size; + array->alloc_align = align; +} + +static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) +{ + return array->list + idx * array->elem_size; +} + +static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) +{ + return (unsigned long)(ptr - array->list) / array->elem_size; +} + +/* * Structures */ @@ -536,15 +566,17 @@ typedef u16 hda_nid_t; /* bus operators */ struct hda_bus_ops { /* send a single command */ - int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct, - unsigned int verb, unsigned int parm); + int (*command)(struct hda_bus *bus, unsigned int cmd); /* get a response from the last command */ - unsigned int (*get_response)(struct hda_codec *codec); + unsigned int (*get_response)(struct hda_bus *bus); /* free the private data */ void (*private_free)(struct hda_bus *); + /* attach a PCM stream */ + int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, + struct hda_pcm *pcm); #ifdef CONFIG_SND_HDA_POWER_SAVE /* notify power-up/down from codec to controller */ - void (*pm_notify)(struct hda_codec *codec); + void (*pm_notify)(struct hda_bus *bus); #endif }; @@ -553,6 +585,7 @@ struct hda_bus_template { void *private_data; struct pci_dev *pci; const char *modelname; + int *power_save; struct hda_bus_ops ops; }; @@ -569,6 +602,7 @@ struct hda_bus { void *private_data; struct pci_dev *pci; const char *modelname; + int *power_save; struct hda_bus_ops ops; /* codec linked list */ @@ -581,10 +615,12 @@ struct hda_bus { /* unsolicited event queue */ struct hda_bus_unsolicited *unsol; - struct snd_info_entry *proc; + /* assigned PCMs */ + DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); /* misc op flags */ unsigned int needs_damn_long_delay :1; + unsigned int shutdown :1; /* being unloaded */ }; /* @@ -604,6 +640,16 @@ struct hda_codec_preset { int (*patch)(struct hda_codec *codec); }; +struct hda_codec_preset_list { + const struct hda_codec_preset *preset; + struct module *owner; + struct list_head list; +}; + +/* initial hook */ +int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset); +int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset); + /* ops set by the preset patch */ struct hda_codec_ops { int (*build_controls)(struct hda_codec *codec); @@ -635,10 +681,7 @@ struct hda_amp_info { struct hda_cache_rec { u16 hash[64]; /* hash table for index */ - unsigned int num_entries; /* number of assigned entries */ - unsigned int size; /* allocated size */ - unsigned int record_size; /* record size (including header) */ - void *buffer; /* hash table entries */ + struct snd_array buf; /* record entries */ }; /* PCM callbacks */ @@ -680,7 +723,8 @@ struct hda_pcm { char *name; struct hda_pcm_stream stream[2]; unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */ - int device; /* assigned device number */ + int device; /* device number to assign */ + struct snd_pcm *pcm; /* assigned PCM instance */ }; /* codec information */ @@ -699,6 +743,9 @@ struct hda_codec { /* detected preset */ const struct hda_codec_preset *preset; + struct module *owner; + const char *name; /* codec name */ + const char *modelname; /* model name for preset */ /* set by patch */ struct hda_codec_ops patch_ops; @@ -718,6 +765,8 @@ struct hda_codec { hda_nid_t start_nid; u32 *wcaps; + struct snd_array mixers; /* list of assigned mixer elements */ + struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */ @@ -727,7 +776,11 @@ struct hda_codec { unsigned int spdif_in_enable; /* SPDIF input enable? */ hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ +#ifdef CONFIG_SND_HDA_HWDEP struct snd_hwdep *hwdep; /* assigned hwdep device */ + struct snd_array init_verbs; /* additional init verbs */ + struct snd_array hints; /* additional hints */ +#endif /* misc flags */ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each @@ -740,6 +793,10 @@ struct hda_codec { int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ #endif + + /* codec-specific additional proc output */ + void (*proc_widget_hook)(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid); }; /* direction */ @@ -754,7 +811,7 @@ enum { int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - struct hda_codec **codecp); + int do_init, struct hda_codec **codecp); /* * low level functions @@ -799,11 +856,13 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec); * Mixer */ int snd_hda_build_controls(struct hda_bus *bus); +int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ 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, u32 stream_tag, int channel_id, int format); @@ -812,8 +871,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, unsigned int format, unsigned int maxbps); -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp); int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, unsigned int format); @@ -831,18 +888,38 @@ int snd_hda_resume(struct hda_bus *bus); #endif /* + * get widget information + */ +const char *snd_hda_get_jack_connectivity(u32 cfg); +const char *snd_hda_get_jack_type(u32 cfg); +const char *snd_hda_get_jack_location(u32 cfg); + +/* * power saving */ #ifdef CONFIG_SND_HDA_POWER_SAVE 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 -int snd_hda_codecs_inuse(struct hda_bus *bus); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} #define snd_hda_codec_needs_resume(codec) 1 -#define snd_hda_codecs_inuse(bus) 1 +#endif + +/* + * Codec modularization + */ + +/* Export symbols only for communication with codec drivers; + * When built in kernel, all HD-audio drivers are supposed to be statically + * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be + * exported unless it's built as a module. + */ +#ifdef MODULE +#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym) +#else +#define EXPORT_SYMBOL_HDA(sym) #endif #endif /* __SOUND_HDA_CODEC_H */ diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c new file mode 100644 index 000000000000..fcad5ec31773 --- /dev/null +++ b/sound/pci/hda/hda_eld.c @@ -0,0 +1,590 @@ +/* + * Generic routines and proc interface for ELD(EDID Like Data) information + * + * Copyright(c) 2008 Intel Corporation. + * + * Authors: + * Wu Fengguang <wfg@linux.intel.com> + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <sound/core.h> +#include <asm/unaligned.h> +#include "hda_codec.h" +#include "hda_local.h" + +enum eld_versions { + ELD_VER_CEA_861D = 2, + ELD_VER_PARTIAL = 31, +}; + +enum cea_edid_versions { + CEA_EDID_VER_NONE = 0, + CEA_EDID_VER_CEA861 = 1, + CEA_EDID_VER_CEA861A = 2, + CEA_EDID_VER_CEA861BCD = 3, + CEA_EDID_VER_RESERVED = 4, +}; + +static char *cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +static char *eld_connection_type_names[4] = { + "HDMI", + "DisplayPort", + "2-reserved", + "3-reserved" +}; + +enum cea_audio_coding_types { + AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0, + AUDIO_CODING_TYPE_LPCM = 1, + AUDIO_CODING_TYPE_AC3 = 2, + AUDIO_CODING_TYPE_MPEG1 = 3, + AUDIO_CODING_TYPE_MP3 = 4, + AUDIO_CODING_TYPE_MPEG2 = 5, + AUDIO_CODING_TYPE_AACLC = 6, + AUDIO_CODING_TYPE_DTS = 7, + AUDIO_CODING_TYPE_ATRAC = 8, + AUDIO_CODING_TYPE_SACD = 9, + AUDIO_CODING_TYPE_EAC3 = 10, + AUDIO_CODING_TYPE_DTS_HD = 11, + AUDIO_CODING_TYPE_MLP = 12, + AUDIO_CODING_TYPE_DST = 13, + AUDIO_CODING_TYPE_WMAPRO = 14, + AUDIO_CODING_TYPE_REF_CXT = 15, + /* also include valid xtypes below */ + AUDIO_CODING_TYPE_HE_AAC = 15, + AUDIO_CODING_TYPE_HE_AAC2 = 16, + AUDIO_CODING_TYPE_MPEG_SURROUND = 17, +}; + +enum cea_audio_coding_xtypes { + AUDIO_CODING_XTYPE_HE_REF_CT = 0, + AUDIO_CODING_XTYPE_HE_AAC = 1, + AUDIO_CODING_XTYPE_HE_AAC2 = 2, + AUDIO_CODING_XTYPE_MPEG_SURROUND = 3, + AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, +}; + +static char *cea_audio_coding_type_names[] = { + /* 0 */ "undefined", + /* 1 */ "LPCM", + /* 2 */ "AC-3", + /* 3 */ "MPEG1", + /* 4 */ "MP3", + /* 5 */ "MPEG2", + /* 6 */ "AAC-LC", + /* 7 */ "DTS", + /* 8 */ "ATRAC", + /* 9 */ "DSD (One Bit Audio)", + /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", + /* 11 */ "DTS-HD", + /* 12 */ "MLP (Dolby TrueHD)", + /* 13 */ "DST", + /* 14 */ "WMAPro", + /* 15 */ "HE-AAC", + /* 16 */ "HE-AACv2", + /* 17 */ "MPEG Surround", +}; + +/* + * The following two lists are shared between + * - HDMI audio InfoFrame (source to sink) + * - CEA E-EDID Extension (sink to source) + */ + +/* + * SS1:SS0 index => sample size + */ +static int cea_sample_sizes[4] = { + 0, /* 0: Refer to Stream Header */ + AC_SUPPCM_BITS_16, /* 1: 16 bits */ + AC_SUPPCM_BITS_20, /* 2: 20 bits */ + AC_SUPPCM_BITS_24, /* 3: 24 bits */ +}; + +/* + * SF2:SF1:SF0 index => sampling frequency + */ +static int cea_sampling_frequencies[8] = { + 0, /* 0: Refer to Stream Header */ + SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ + SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ + SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ + SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ + SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ + SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ + SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ +}; + +static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, + int byte_index) +{ + unsigned int val; + + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_HDMI_ELDD, byte_index); + +#ifdef BE_PARANOID + printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val); +#endif + + if ((val & AC_ELDD_ELD_VALID) == 0) { + snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n", + byte_index); + val = 0; + } + + return val & AC_ELDD_ELD_DATA; +} + +#define GRAB_BITS(buf, byte, lowbit, bits) \ +({ \ + BUILD_BUG_ON(lowbit > 7); \ + BUILD_BUG_ON(bits > 8); \ + BUILD_BUG_ON(bits <= 0); \ + \ + (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ +}) + +static void hdmi_update_short_audio_desc(struct cea_sad *a, + const unsigned char *buf) +{ + int i; + int val; + + val = GRAB_BITS(buf, 1, 0, 7); + a->rates = 0; + for (i = 0; i < 7; i++) + if (val & (1 << i)) + a->rates |= cea_sampling_frequencies[i + 1]; + + a->channels = GRAB_BITS(buf, 0, 0, 3); + a->channels++; + + a->format = GRAB_BITS(buf, 0, 3, 4); + switch (a->format) { + case AUDIO_CODING_TYPE_REF_STREAM_HEADER: + snd_printd(KERN_INFO + "HDMI: audio coding type 0 not expected\n"); + break; + + case AUDIO_CODING_TYPE_LPCM: + val = GRAB_BITS(buf, 2, 0, 3); + a->sample_bits = 0; + for (i = 0; i < 3; i++) + if (val & (1 << i)) + a->sample_bits |= cea_sample_sizes[i + 1]; + break; + + case AUDIO_CODING_TYPE_AC3: + case AUDIO_CODING_TYPE_MPEG1: + case AUDIO_CODING_TYPE_MP3: + case AUDIO_CODING_TYPE_MPEG2: + case AUDIO_CODING_TYPE_AACLC: + case AUDIO_CODING_TYPE_DTS: + case AUDIO_CODING_TYPE_ATRAC: + a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); + a->max_bitrate *= 8000; + break; + + case AUDIO_CODING_TYPE_SACD: + break; + + case AUDIO_CODING_TYPE_EAC3: + break; + + case AUDIO_CODING_TYPE_DTS_HD: + break; + + case AUDIO_CODING_TYPE_MLP: + break; + + case AUDIO_CODING_TYPE_DST: + break; + + case AUDIO_CODING_TYPE_WMAPRO: + a->profile = GRAB_BITS(buf, 2, 0, 3); + break; + + case AUDIO_CODING_TYPE_REF_CXT: + a->format = GRAB_BITS(buf, 2, 3, 5); + if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || + a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { + snd_printd(KERN_INFO + "HDMI: audio coding xtype %d not expected\n", + a->format); + a->format = 0; + } else + a->format += AUDIO_CODING_TYPE_HE_AAC - + AUDIO_CODING_XTYPE_HE_AAC; + break; + } +} + +/* + * Be careful, ELD buf could be totally rubbish! + */ +static int hdmi_update_eld(struct hdmi_eld *e, + const unsigned char *buf, int size) +{ + int mnl; + int i; + + e->eld_ver = GRAB_BITS(buf, 0, 3, 5); + if (e->eld_ver != ELD_VER_CEA_861D && + e->eld_ver != ELD_VER_PARTIAL) { + snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n", + e->eld_ver); + goto out_fail; + } + + e->eld_size = size; + e->baseline_len = GRAB_BITS(buf, 2, 0, 8); + mnl = GRAB_BITS(buf, 4, 0, 5); + e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); + + e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); + e->support_ai = GRAB_BITS(buf, 5, 1, 1); + e->conn_type = GRAB_BITS(buf, 5, 2, 2); + e->sad_count = GRAB_BITS(buf, 5, 4, 4); + + e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; + e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); + + e->port_id = get_unaligned_le64(buf + 8); + + /* not specified, but the spec's tendency is little endian */ + e->manufacture_id = get_unaligned_le16(buf + 16); + e->product_id = get_unaligned_le16(buf + 18); + + if (mnl > ELD_MAX_MNL) { + snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl); + goto out_fail; + } else if (ELD_FIXED_BYTES + mnl > size) { + snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl); + goto out_fail; + } else + strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl); + + for (i = 0; i < e->sad_count; i++) { + if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { + snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i); + goto out_fail; + } + hdmi_update_short_audio_desc(e->sad + i, + buf + ELD_FIXED_BYTES + mnl + 3 * i); + } + + return 0; + +out_fail: + e->eld_ver = 0; + 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); + eldv = (present & AC_PINSENSE_ELDV); + present = (present & AC_PINSENSE_PRESENCE); + +#ifdef CONFIG_SND_DEBUG_VERBOSE + printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n", + !!present, !!eldv); +#endif + + return eldv && present; +} + +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); +} + +int snd_hdmi_get_eld(struct hdmi_eld *eld, + struct hda_codec *codec, hda_nid_t nid) +{ + int i; + int ret; + int size; + unsigned char *buf; + + if (!hdmi_eld_valid(codec, nid)) + return -ENOENT; + + size = snd_hdmi_get_eld_size(codec, nid); + if (size == 0) { + /* wfg: workaround for ASUS P5E-VM HDMI board */ + snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n"); + size = 128; + } + if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) { + snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size); + return -ERANGE; + } + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < size; i++) + buf[i] = hdmi_get_eld_byte(codec, nid, i); + + ret = hdmi_update_eld(eld, buf, size); + + kfree(buf); + return ret; +} + +static void hdmi_show_short_audio_desc(struct cea_sad *a) +{ + char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; + char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; + + if (!a->format) + return; + + snd_print_pcm_rates(a->rates, buf, sizeof(buf)); + + if (a->format == AUDIO_CODING_TYPE_LPCM) + snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8)); + else if (a->max_bitrate) + snprintf(buf2, sizeof(buf2), + ", max bitrate = %d", a->max_bitrate); + else + buf2[0] = '\0'; + + printk(KERN_INFO "HDMI: supports coding type %s:" + " channels = %d, rates =%s%s\n", + cea_audio_coding_type_names[a->format], + a->channels, + buf, + buf2); +} + +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += snprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} + +void snd_hdmi_show_eld(struct hdmi_eld *e) +{ + int i; + + printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n", + e->monitor_name, + eld_connection_type_names[e->conn_type]); + + if (e->spk_alloc) { + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + printk(KERN_INFO "HDMI: available speakers:%s\n", buf); + } + + for (i = 0; i < e->sad_count; i++) + hdmi_show_short_audio_desc(e->sad + i); +} + +#ifdef CONFIG_PROC_FS + +static void hdmi_print_sad_info(int i, struct cea_sad *a, + struct snd_info_buffer *buffer) +{ + char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; + + snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", + i, a->format, cea_audio_coding_type_names[a->format]); + snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); + + snd_print_pcm_rates(a->rates, buf, sizeof(buf)); + snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); + + if (a->format == AUDIO_CODING_TYPE_LPCM) { + snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); + snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", + i, a->sample_bits, buf); + } + + if (a->max_bitrate) + snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", + i, a->max_bitrate); + + if (a->profile) + snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); +} + +static void hdmi_print_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_eld *e = entry->private_data; + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + int i; + static char *eld_versoin_names[32] = { + "reserved", + "reserved", + "CEA-861D or below", + [3 ... 30] = "reserved", + [31] = "partial" + }; + static char *cea_edid_version_names[8] = { + "no CEA EDID Timing Extension block present", + "CEA-861", + "CEA-861-A", + "CEA-861-B, C or D", + [4 ... 7] = "reserved" + }; + + 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]); + snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, + eld_versoin_names[e->eld_ver]); + snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, + cea_edid_version_names[e->cea_edid_ver]); + snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); + snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); + snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); + snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); + snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); + snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); + + snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); + + snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); + + for (i = 0; i < e->sad_count; i++) + hdmi_print_sad_info(i, e->sad + i, buffer); +} + +static void hdmi_write_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_eld *e = entry->private_data; + char line[64]; + char name[64]; + char *sname; + long long val; + int n; + + while (!snd_info_get_line(buffer, line, sizeof(line))) { + if (sscanf(line, "%s %llx", name, &val) != 2) + continue; + /* + * We don't allow modification to these fields: + * monitor_name manufacture_id product_id + * eld_version edid_version + */ + if (!strcmp(name, "connection_type")) + e->conn_type = val; + else if (!strcmp(name, "port_id")) + e->port_id = val; + else if (!strcmp(name, "support_hdcp")) + e->support_hdcp = val; + else if (!strcmp(name, "support_ai")) + e->support_ai = val; + else if (!strcmp(name, "audio_sync_delay")) + e->aud_synch_delay = val; + else if (!strcmp(name, "speakers")) + e->spk_alloc = val; + else if (!strcmp(name, "sad_count")) + e->sad_count = val; + else if (!strncmp(name, "sad", 3)) { + sname = name + 4; + n = name[3] - '0'; + if (name[4] >= '0' && name[4] <= '9') { + sname++; + n = 10 * n + name[4] - '0'; + } + if (n < 0 || n > 31) /* double the CEA limit */ + continue; + if (!strcmp(sname, "_coding_type")) + e->sad[n].format = val; + else if (!strcmp(sname, "_channels")) + e->sad[n].channels = val; + else if (!strcmp(sname, "_rates")) + e->sad[n].rates = val; + else if (!strcmp(sname, "_bits")) + e->sad[n].sample_bits = val; + else if (!strcmp(sname, "_max_bitrate")) + e->sad[n].max_bitrate = val; + else if (!strcmp(sname, "_profile")) + e->sad[n].profile = val; + if (n >= e->sad_count) + e->sad_count = n + 1; + } + } +} + + +int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld) +{ + char name[32]; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "eld#%d", codec->addr); + err = snd_card_proc_new(codec->bus->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, eld, hdmi_print_eld_info); + entry->c.text.write = hdmi_write_eld_info; + entry->mode |= S_IWUSR; + eld->proc_entry = entry; + + return 0; +} + +void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) +{ + if (!codec->bus->shutdown && eld->proc_entry) { + snd_device_free(codec->bus->card, eld->proc_entry); + eld->proc_entry = NULL; + } +} + +#endif /* CONFIG_PROC_FS */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 0ca30894f7c6..65745e96dc70 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -723,7 +723,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); - if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + if (err < 0) return err; created = 1; } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && @@ -732,7 +733,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); - if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + if (err < 0) return err; created = 1; } @@ -745,14 +747,16 @@ 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); - if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + if (err < 0) return err; created = 1; } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && (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); - if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + if (err < 0) return err; created = 1; } @@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec) } /* create input MUX if multiple sources are available */ - if ((err = snd_ctl_add(codec->bus->card, - snd_ctl_new1(&cap_sel, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec)); + if (err < 0) return err; /* no volume control? */ @@ -867,8 +871,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); - if ((err = snd_ctl_add(codec->bus->card, - snd_ctl_new1(&knew, codec))) < 0) + err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec)); + if (err < 0) return err; } @@ -1097,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) snd_hda_generic_free(codec); return err; } +EXPORT_SYMBOL(snd_hda_parse_generic_codec); diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 6e18a422d993..300ab407cf42 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -23,10 +23,12 @@ #include <linux/pci.h> #include <linux/compat.h> #include <linux/mutex.h> +#include <linux/ctype.h> #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" #include <sound/hda_hwdep.h> +#include <sound/minors.h> /* * write/read an out-of-bound verb @@ -95,7 +97,26 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) return 0; } -int __devinit snd_hda_create_hwdep(struct hda_codec *codec) +static void clear_hwdep_elements(struct hda_codec *codec) +{ + char **head; + int i; + + /* clear init verbs */ + snd_array_free(&codec->init_verbs); + /* clear hints */ + head = codec->hints.list; + for (i = 0; i < codec->hints.used; i++, head++) + kfree(*head); + snd_array_free(&codec->hints); +} + +static void hwdep_free(struct snd_hwdep *hwdep) +{ + clear_hwdep_elements(hwdep->private_data); +} + +int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) { char hwname[16]; struct snd_hwdep *hwdep; @@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec) sprintf(hwdep->name, "HDA Codec %d", codec->addr); hwdep->iface = SNDRV_HWDEP_IFACE_HDA; hwdep->private_data = codec; + hwdep->private_free = hwdep_free; hwdep->exclusive = 1; hwdep->ops.open = hda_hwdep_open; @@ -117,5 +139,215 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec) hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; #endif + snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32); + snd_array_init(&codec->hints, sizeof(char *), 32); + return 0; } + +#ifdef CONFIG_SND_HDA_RECONFIG + +/* + * sysfs interface + */ + +static int clear_codec(struct hda_codec *codec) +{ + snd_hda_codec_reset(codec); + clear_hwdep_elements(codec); + return 0; +} + +static int reconfig_codec(struct hda_codec *codec) +{ + int err; + + snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); + snd_hda_codec_reset(codec); + err = snd_hda_codec_configure(codec); + if (err < 0) + return err; + /* rebuild PCMs */ + err = snd_hda_codec_build_pcms(codec); + if (err < 0) + return err; + /* rebuild mixers */ + err = snd_hda_codec_build_controls(codec); + if (err < 0) + return err; + return 0; +} + +/* + * allocate a string at most len chars, and remove the trailing EOL + */ +static char *kstrndup_noeol(const char *src, size_t len) +{ + char *s = kstrndup(src, len, GFP_KERNEL); + char *p; + if (!s) + return NULL; + p = strchr(s, '\n'); + if (p) + *p = 0; + return s; +} + +#define CODEC_INFO_SHOW(type) \ +static ssize_t type##_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; \ + return sprintf(buf, "0x%x\n", codec->type); \ +} + +#define CODEC_INFO_STR_SHOW(type) \ +static ssize_t type##_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; \ + return sprintf(buf, "%s\n", \ + codec->type ? codec->type : ""); \ +} + +CODEC_INFO_SHOW(vendor_id); +CODEC_INFO_SHOW(subsystem_id); +CODEC_INFO_SHOW(revision_id); +CODEC_INFO_SHOW(afg); +CODEC_INFO_SHOW(mfg); +CODEC_INFO_STR_SHOW(name); +CODEC_INFO_STR_SHOW(modelname); + +#define CODEC_INFO_STORE(type) \ +static ssize_t type##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ + struct hda_codec *codec = hwdep->private_data; \ + char *after; \ + codec->type = simple_strtoul(buf, &after, 0); \ + return count; \ +} + +#define CODEC_INFO_STR_STORE(type) \ +static ssize_t type##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ + struct hda_codec *codec = hwdep->private_data; \ + char *s = kstrndup_noeol(buf, 64); \ + if (!s) \ + return -ENOMEM; \ + kfree(codec->type); \ + codec->type = s; \ + return count; \ +} + +CODEC_INFO_STORE(vendor_id); +CODEC_INFO_STORE(subsystem_id); +CODEC_INFO_STORE(revision_id); +CODEC_INFO_STR_STORE(name); +CODEC_INFO_STR_STORE(modelname); + +#define CODEC_ACTION_STORE(type) \ +static ssize_t type##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ + struct hda_codec *codec = hwdep->private_data; \ + int err = 0; \ + if (*buf) \ + err = type##_codec(codec); \ + return err < 0 ? err : count; \ +} + +CODEC_ACTION_STORE(reconfig); +CODEC_ACTION_STORE(clear); + +static ssize_t init_verbs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + char *p; + struct hda_verb verb, *v; + + verb.nid = simple_strtoul(buf, &p, 0); + verb.verb = simple_strtoul(p, &p, 0); + verb.param = simple_strtoul(p, &p, 0); + if (!verb.nid || !verb.verb || !verb.param) + return -EINVAL; + v = snd_array_new(&codec->init_verbs); + if (!v) + return -ENOMEM; + *v = verb; + return count; +} + +static ssize_t hints_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct snd_hwdep *hwdep = dev_get_drvdata(dev); + struct hda_codec *codec = hwdep->private_data; + char *p; + char **hint; + + if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n') + return count; + p = kstrndup_noeol(buf, 1024); + if (!p) + return -ENOMEM; + hint = snd_array_new(&codec->hints); + if (!hint) { + kfree(p); + return -ENOMEM; + } + *hint = p; + return count; +} + +#define CODEC_ATTR_RW(type) \ + __ATTR(type, 0644, type##_show, type##_store) +#define CODEC_ATTR_RO(type) \ + __ATTR_RO(type) +#define CODEC_ATTR_WO(type) \ + __ATTR(type, 0200, NULL, type##_store) + +static struct device_attribute codec_attrs[] = { + CODEC_ATTR_RW(vendor_id), + CODEC_ATTR_RW(subsystem_id), + CODEC_ATTR_RW(revision_id), + CODEC_ATTR_RO(afg), + CODEC_ATTR_RO(mfg), + CODEC_ATTR_RW(name), + CODEC_ATTR_RW(modelname), + CODEC_ATTR_WO(init_verbs), + CODEC_ATTR_WO(hints), + CODEC_ATTR_WO(reconfig), + CODEC_ATTR_WO(clear), +}; + +/* + * create sysfs files on hwdep directory + */ +int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) +{ + struct snd_hwdep *hwdep = codec->hwdep; + int i; + + for (i = 0; i < ARRAY_SIZE(codec_attrs); i++) + snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, + hwdep->device, &codec_attrs[i]); + return 0; +} + +#endif /* CONFIG_SND_HDA_RECONFIG */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9f316c1b2790..f04de115ee11 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -45,6 +45,7 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/mutex.h> +#include <linux/reboot.h> #include <sound/core.h> #include <sound/initval.h> #include "hda_codec.h" @@ -57,6 +58,7 @@ static char *model[SNDRV_CARDS]; static int position_fix[SNDRV_CARDS]; 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; @@ -75,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); +module_param_array(probe_only, bool, NULL, 0444); +MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); @@ -82,7 +86,10 @@ module_param(enable_msi, int, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); #ifdef CONFIG_SND_HDA_POWER_SAVE -/* power_save option is defined in hda_codec.c */ +static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; +module_param(power_save, int, 0644); +MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " + "(in second, 0 = disable)."); /* reset the HD-audio controller in power save mode. * this may give more power-saving, but will take longer time to @@ -291,6 +298,8 @@ enum { /* Define VIA HD Audio Device ID*/ #define VIA_HDAC_DEVICE_ID 0x3288 +/* HD Audio class code */ +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 /* */ @@ -391,12 +400,16 @@ struct azx { unsigned int msi :1; unsigned int irq_pending_warned :1; unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */ + unsigned int probing :1; /* codec probing phase */ /* for debugging */ unsigned int last_cmd; /* last issued command (to sync) */ /* for pending irqs */ struct work_struct irq_pending_work; + + /* reboot notifier (for mysterious hangup problem at power-down) */ + struct notifier_block reboot_notifier; }; /* driver types */ @@ -410,6 +423,7 @@ enum { AZX_DRIVER_ULI, AZX_DRIVER_NVIDIA, AZX_DRIVER_TERA, + AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -423,6 +437,7 @@ static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ULI] = "HDA ULI M5461", [AZX_DRIVER_NVIDIA] = "HDA NVidia", [AZX_DRIVER_TERA] = "HDA Teradici", + [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; /* @@ -523,9 +538,9 @@ static void azx_free_cmd_io(struct azx *chip) } /* send a command */ -static int azx_corb_send_cmd(struct hda_codec *codec, u32 val) +static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; unsigned int wp; /* add command to corb */ @@ -573,9 +588,9 @@ static void azx_update_rirb(struct azx *chip) } /* receive a response */ -static unsigned int azx_rirb_get_response(struct hda_codec *codec) +static unsigned int azx_rirb_get_response(struct hda_bus *bus) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; unsigned long timeout; again: @@ -592,7 +607,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) } if (time_after(jiffies, timeout)) break; - if (codec->bus->needs_damn_long_delay) + if (bus->needs_damn_long_delay) msleep(2); /* temporary workaround */ else { udelay(10); @@ -620,6 +635,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) 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 + * slot. Better to return an error and reset the system. + */ + return -1; + } + snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " "switching to single_cmd mode: last cmd=0x%08x\n", chip->last_cmd); @@ -642,9 +665,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) */ /* send a command */ -static int azx_single_send_cmd(struct hda_codec *codec, u32 val) +static int azx_single_send_cmd(struct hda_bus *bus, u32 val) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; int timeout = 50; while (timeout--) { @@ -667,9 +690,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val) } /* receive a response */ -static unsigned int azx_single_get_response(struct hda_codec *codec) +static unsigned int azx_single_get_response(struct hda_bus *bus) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; int timeout = 50; while (timeout--) { @@ -692,38 +715,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec) */ /* send a command */ -static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, - unsigned int para) +static int azx_send_cmd(struct hda_bus *bus, unsigned int val) { - struct azx *chip = codec->bus->private_data; - u32 val; - - val = (u32)(codec->addr & 0x0f) << 28; - val |= (u32)direct << 27; - val |= (u32)nid << 20; - val |= verb << 8; - val |= para; - chip->last_cmd = val; + struct azx *chip = bus->private_data; + chip->last_cmd = val; if (chip->single_cmd) - return azx_single_send_cmd(codec, val); + return azx_single_send_cmd(bus, val); else - return azx_corb_send_cmd(codec, val); + return azx_corb_send_cmd(bus, val); } /* get a response */ -static unsigned int azx_get_response(struct hda_codec *codec) +static unsigned int azx_get_response(struct hda_bus *bus) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; if (chip->single_cmd) - return azx_single_get_response(codec); + return azx_single_get_response(bus); else - return azx_rirb_get_response(codec); + return azx_rirb_get_response(bus); } #ifdef CONFIG_SND_HDA_POWER_SAVE -static void azx_power_notify(struct hda_codec *codec); +static void azx_power_notify(struct hda_bus *bus); #endif /* reset codec link */ @@ -1180,6 +1194,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) return 0; } +/* + * Probe the given codec address + */ +static int probe_codec(struct azx *chip, int addr) +{ + unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | + (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + unsigned int res; + + chip->probing = 1; + azx_send_cmd(chip->bus, cmd); + res = azx_get_response(chip->bus); + chip->probing = 0; + if (res == -1) + return -EIO; + snd_printdd("hda_intel: codec #%d probed OK\n", addr); + return 0; +} + +static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, + struct hda_pcm *cpcm); +static void azx_stop_chip(struct azx *chip); /* * Codec initialization @@ -1190,21 +1226,13 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = { [AZX_DRIVER_TERA] = 1, }; -/* number of slots to probe as default - * this can be different from azx_max_codecs[] -- e.g. some boards - * report wrongly the non-existing 4th slot availability - */ -static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = { - [AZX_DRIVER_ICH] = 3, - [AZX_DRIVER_ATI] = 3, -}; - static int __devinit azx_codec_create(struct azx *chip, const char *model, - unsigned int codec_probe_mask) + unsigned int codec_probe_mask, + int no_init) { struct hda_bus_template bus_temp; - int c, codecs, audio_codecs, err; - int def_slots, max_slots; + int c, codecs, err; + int max_slots; memset(&bus_temp, 0, sizeof(bus_temp)); bus_temp.private_data = chip; @@ -1212,7 +1240,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, bus_temp.pci = chip->pci; bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; + bus_temp.ops.attach_pcm = azx_attach_pcm_stream; #ifdef CONFIG_SND_HDA_POWER_SAVE + bus_temp.power_save = &power_save; bus_temp.ops.pm_notify = azx_power_notify; #endif @@ -1223,33 +1253,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model, if (chip->driver_type == AZX_DRIVER_NVIDIA) chip->bus->needs_damn_long_delay = 1; - codecs = audio_codecs = 0; + codecs = 0; max_slots = azx_max_codecs[chip->driver_type]; if (!max_slots) max_slots = AZX_MAX_CODECS; - def_slots = azx_default_codecs[chip->driver_type]; - if (!def_slots) - def_slots = max_slots; - for (c = 0; c < def_slots; c++) { + + /* First try to probe all given codec slots */ + for (c = 0; c < max_slots; c++) { + if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { + if (probe_codec(chip, c) < 0) { + /* Some BIOSen give you wrong codec addresses + * that don't exist + */ + snd_printk(KERN_WARNING + "hda_intel: Codec #%d probe error; " + "disabling it...\n", c); + chip->codec_mask &= ~(1 << c); + /* More badly, accessing to a non-existing + * codec often screws up the controller chip, + * and distrubs the further communications. + * Thus if an error occurs during probing, + * better to reset the controller chip to + * get back to the sanity state. + */ + azx_stop_chip(chip); + azx_init_chip(chip); + } + } + } + + /* Then create codec instances */ + for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, &codec); + err = snd_hda_codec_new(chip->bus, c, !no_init, &codec); if (err < 0) continue; codecs++; - if (codec->afg) - audio_codecs++; - } - } - if (!audio_codecs) { - /* probe additional slots if no codec is found */ - for (; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { - err = snd_hda_codec_new(chip->bus, c, NULL); - if (err < 0) - continue; - codecs++; - } } } if (!codecs) { @@ -1718,111 +1758,59 @@ static struct snd_pcm_ops azx_pcm_ops = { static void azx_pcm_free(struct snd_pcm *pcm) { - kfree(pcm->private_data); + struct azx_pcm *apcm = pcm->private_data; + if (apcm) { + apcm->chip->pcm[pcm->device] = NULL; + kfree(apcm); + } } -static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec, - struct hda_pcm *cpcm) +static int +azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, + struct hda_pcm *cpcm) { - int err; + struct azx *chip = bus->private_data; struct snd_pcm *pcm; struct azx_pcm *apcm; + int pcm_dev = cpcm->device; + int s, err; - /* if no substreams are defined for both playback and capture, - * it's just a placeholder. ignore it. - */ - if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) - return 0; - - if (snd_BUG_ON(!cpcm->name)) + if (pcm_dev >= AZX_MAX_PCMS) { + snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n", + pcm_dev); return -EINVAL; - - err = snd_pcm_new(chip->card, cpcm->name, cpcm->device, - cpcm->stream[0].substreams, - cpcm->stream[1].substreams, + } + if (chip->pcm[pcm_dev]) { + snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev); + return -EBUSY; + } + err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, + cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams, + cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams, &pcm); if (err < 0) return err; strcpy(pcm->name, cpcm->name); - apcm = kmalloc(sizeof(*apcm), GFP_KERNEL); + apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); if (apcm == NULL) return -ENOMEM; apcm->chip = chip; apcm->codec = codec; - apcm->hinfo[0] = &cpcm->stream[0]; - apcm->hinfo[1] = &cpcm->stream[1]; pcm->private_data = apcm; pcm->private_free = azx_pcm_free; - if (cpcm->stream[0].substreams) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops); - if (cpcm->stream[1].substreams) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops); + if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) + pcm->dev_class = SNDRV_PCM_CLASS_MODEM; + chip->pcm[pcm_dev] = pcm; + cpcm->pcm = pcm; + for (s = 0; s < 2; s++) { + apcm->hinfo[s] = &cpcm->stream[s]; + if (cpcm->stream[s].substreams) + snd_pcm_set_ops(pcm, s, &azx_pcm_ops); + } + /* buffer pre-allocation */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(chip->pci), 1024 * 64, 32 * 1024 * 1024); - chip->pcm[cpcm->device] = pcm; - return 0; -} - -static int __devinit azx_pcm_create(struct azx *chip) -{ - static const char *dev_name[HDA_PCM_NTYPES] = { - "Audio", "SPDIF", "HDMI", "Modem" - }; - /* starting device index for each PCM type */ - static int dev_idx[HDA_PCM_NTYPES] = { - [HDA_PCM_TYPE_AUDIO] = 0, - [HDA_PCM_TYPE_SPDIF] = 1, - [HDA_PCM_TYPE_HDMI] = 3, - [HDA_PCM_TYPE_MODEM] = 6 - }; - /* normal audio device indices; not linear to keep compatibility */ - static int audio_idx[4] = { 0, 2, 4, 5 }; - struct hda_codec *codec; - int c, err; - int num_devs[HDA_PCM_NTYPES]; - - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - return err; - - /* create audio PCMs */ - memset(num_devs, 0, sizeof(num_devs)); - list_for_each_entry(codec, &chip->bus->codec_list, list) { - for (c = 0; c < codec->num_pcms; c++) { - struct hda_pcm *cpcm = &codec->pcm_info[c]; - int type = cpcm->pcm_type; - switch (type) { - case HDA_PCM_TYPE_AUDIO: - if (num_devs[type] >= ARRAY_SIZE(audio_idx)) { - snd_printk(KERN_WARNING - "Too many audio devices\n"); - continue; - } - cpcm->device = audio_idx[num_devs[type]]; - break; - case HDA_PCM_TYPE_SPDIF: - case HDA_PCM_TYPE_HDMI: - case HDA_PCM_TYPE_MODEM: - if (num_devs[type]) { - snd_printk(KERN_WARNING - "%s already defined\n", - dev_name[type]); - continue; - } - cpcm->device = dev_idx[type]; - break; - default: - snd_printk(KERN_WARNING - "Invalid PCM type %d\n", type); - continue; - } - num_devs[type]++; - err = create_codec_pcm(chip, codec, cpcm); - if (err < 0) - return err; - } - } return 0; } @@ -1899,13 +1887,13 @@ static void azx_stop_chip(struct azx *chip) #ifdef CONFIG_SND_HDA_POWER_SAVE /* power-up/down the controller */ -static void azx_power_notify(struct hda_codec *codec) +static void azx_power_notify(struct hda_bus *bus) { - struct azx *chip = codec->bus->private_data; + struct azx *chip = bus->private_data; struct hda_codec *c; int power_on = 0; - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_entry(c, &bus->codec_list, list) { if (c->power_on) { power_on = 1; break; @@ -1922,6 +1910,18 @@ static void azx_power_notify(struct hda_codec *codec) /* * power management */ + +static int snd_hda_codecs_inuse(struct hda_bus *bus) +{ + struct hda_codec *codec; + + list_for_each_entry(codec, &bus->codec_list, list) { + if (snd_hda_codec_needs_resume(codec)) + return 1; + } + return 0; +} + static int azx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); @@ -1947,13 +1947,16 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state) return 0; } +static int azx_resume_early(struct pci_dev *pci) +{ + return pci_restore_state(pci); +} + static int azx_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct azx *chip = card->private_data; - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); if (pci_enable_device(pci) < 0) { printk(KERN_ERR "hda-intel: pci_enable_device failed, " "disabling device\n"); @@ -1979,12 +1982,36 @@ static int azx_resume(struct pci_dev *pci) /* + * reboot notifier for hang-up problem at power-down + */ +static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) +{ + struct azx *chip = container_of(nb, struct azx, reboot_notifier); + azx_stop_chip(chip); + return NOTIFY_OK; +} + +static void azx_notifier_register(struct azx *chip) +{ + chip->reboot_notifier.notifier_call = azx_halt; + register_reboot_notifier(&chip->reboot_notifier); +} + +static void azx_notifier_unregister(struct azx *chip) +{ + if (chip->reboot_notifier.notifier_call) + unregister_reboot_notifier(&chip->reboot_notifier); +} + +/* * destructor */ static int azx_free(struct azx *chip) { int i; + azx_notifier_unregister(chip); + if (chip->initialized) { azx_clear_irq_pending(chip); for (i = 0; i < chip->num_streams; i++) @@ -2067,6 +2094,10 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = { SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01), SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01), SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01), + /* broken BIOS */ + SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01), + /* including bogus ALC268 in slot#2 that conflicts with ALC888 */ + SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01), {} }; @@ -2158,7 +2189,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, } chip->addr = pci_resource_start(pci, 0); - chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0)); + chip->remap_addr = pci_ioremap_bar(pci, 0); if (chip->remap_addr == NULL) { snd_printk(KERN_ERR SFX "ioremap error\n"); err = -ENXIO; @@ -2201,6 +2232,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->playback_streams = ATIHDMI_NUM_PLAYBACK; chip->capture_streams = ATIHDMI_NUM_CAPTURE; break; + case AZX_DRIVER_GENERIC: default: chip->playback_streams = ICH6_NUM_PLAYBACK; chip->capture_streams = ICH6_NUM_CAPTURE; @@ -2310,47 +2342,42 @@ static int __devinit azx_probe(struct pci_dev *pci, } err = azx_create(card, pci, dev, pci_id->driver_data, &chip); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto out_free; card->private_data = chip; /* create codec instances */ - err = azx_codec_create(chip, model[dev], probe_mask[dev]); - if (err < 0) { - snd_card_free(card); - return err; - } + err = azx_codec_create(chip, model[dev], probe_mask[dev], + probe_only[dev]); + if (err < 0) + goto out_free; /* create PCM streams */ - err = azx_pcm_create(chip); - if (err < 0) { - snd_card_free(card); - return err; - } + err = snd_hda_build_pcms(chip->bus); + if (err < 0) + goto out_free; /* create mixer controls */ err = azx_mixer_create(chip); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto out_free; snd_card_set_dev(card, &pci->dev); err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto out_free; pci_set_drvdata(pci, card); chip->running = 1; power_down_all_codecs(chip); + azx_notifier_register(chip); dev++; return err; +out_free: + snd_card_free(card); + return err; } static void __devexit azx_remove(struct pci_dev *pci) @@ -2424,6 +2451,11 @@ static struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA }, /* Teradici */ { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA }, + /* AMD Generic, PCI class code and Vendor ID for HD Audio */ + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), + .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, + .class_mask = 0xffffff, + .driver_data = AZX_DRIVER_GENERIC }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); @@ -2436,6 +2468,7 @@ static struct pci_driver driver = { .remove = __devexit_p(azx_remove), #ifdef CONFIG_PM .suspend = azx_suspend, + .resume_early = azx_resume_early, .resume = azx_resume, #endif }; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 7957fefda730..6f2fe0f9fdd8 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -96,6 +96,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name); int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves); +void snd_hda_codec_reset(struct hda_codec *codec); +int snd_hda_codec_configure(struct hda_codec *codec); /* amp value bits */ #define HDA_AMP_MUTE 0x80 @@ -282,6 +284,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec); static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } #endif +#define SND_PRINT_RATES_ADVISED_BUFSIZE 80 +void snd_print_pcm_rates(int pcm, char *buf, int buflen); + +#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 +void snd_print_pcm_bits(int pcm, char *buf, int buflen); + /* * Misc */ @@ -364,17 +372,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, /* amp values */ #define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) #define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) -#define AMP_OUT_MUTE 0xb080 -#define AMP_OUT_UNMUTE 0xb000 -#define AMP_OUT_ZERO 0xb000 +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define AMP_OUT_ZERO 0xb000 /* pinctl values */ #define PIN_IN (AC_PINCTL_IN_EN) -#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) +#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) #define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50) -#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) +#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) #define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80) -#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) -#define PIN_OUT (AC_PINCTL_OUT_EN) +#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) +#define PIN_OUT (AC_PINCTL_OUT_EN) #define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) #define PIN_HP_AMP (AC_PINCTL_HP_EN) @@ -393,10 +401,26 @@ 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); +int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl); +void snd_hda_ctls_clear(struct hda_codec *codec); + /* * hwdep interface */ +#ifdef CONFIG_SND_HDA_HWDEP int snd_hda_create_hwdep(struct hda_codec *codec); +#else +static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; } +#endif + +#ifdef CONFIG_SND_HDA_RECONFIG +int snd_hda_hwdep_add_sysfs(struct hda_codec *codec); +#else +static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec) +{ + return 0; +} +#endif /* * power-management @@ -430,4 +454,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) +/* + * CEA Short Audio Descriptor data + */ +struct cea_sad { + int channels; + int format; /* (format == 0) indicates invalid SAD */ + int rates; + int sample_bits; /* for LPCM */ + int max_bitrate; /* for AC3...ATRAC */ + int profile; /* for WMAPRO */ +}; + +#define ELD_FIXED_BYTES 20 +#define ELD_MAX_MNL 16 +#define ELD_MAX_SAD 16 + +/* + * ELD: EDID Like Data + */ +struct hdmi_eld { + int eld_size; + int baseline_len; + int eld_ver; /* (eld_ver == 0) indicates invalid ELD */ + int cea_edid_ver; + char monitor_name[ELD_MAX_MNL + 1]; + int manufacture_id; + int product_id; + u64 port_id; + int support_hdcp; + int support_ai; + int conn_type; + int aud_synch_delay; + int spk_alloc; + int sad_count; + struct cea_sad sad[ELD_MAX_SAD]; +#ifdef CONFIG_PROC_FS + struct snd_info_entry *proc_entry; +#endif +}; + +int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); +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); +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) +{ + return 0; +} +static inline void snd_hda_eld_proc_free(struct hda_codec *codec, + struct hdmi_eld *eld) +{ +} +#endif + +#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 +void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h deleted file mode 100644 index dfbcfa88da44..000000000000 --- a/sound/pci/hda/hda_patch.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * HDA Patches - included by hda_codec.c - */ - -/* Realtek codecs */ -extern struct hda_codec_preset snd_hda_preset_realtek[]; -/* C-Media codecs */ -extern struct hda_codec_preset snd_hda_preset_cmedia[]; -/* Analog Devices codecs */ -extern struct hda_codec_preset snd_hda_preset_analog[]; -/* SigmaTel codecs */ -extern struct hda_codec_preset snd_hda_preset_sigmatel[]; -/* SiLabs 3054/3055 modem codecs */ -extern struct hda_codec_preset snd_hda_preset_si3054[]; -/* ATI HDMI codecs */ -extern struct hda_codec_preset snd_hda_preset_atihdmi[]; -/* Conexant audio codec */ -extern struct hda_codec_preset snd_hda_preset_conexant[]; -/* VIA codecs */ -extern struct hda_codec_preset snd_hda_preset_via[]; -/* NVIDIA HDMI codecs */ -extern struct hda_codec_preset snd_hda_preset_nvhdmi[]; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 743d77922bce..7ca66d654148 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer, static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm) { - static unsigned int rates[] = { - 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, - 96000, 176400, 192000, 384000 - }; - int i; + char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; pcm &= AC_SUPPCM_RATES; snd_iprintf(buffer, " rates [0x%x]:", pcm); - for (i = 0; i < ARRAY_SIZE(rates); i++) - if (pcm & (1 << i)) - snd_iprintf(buffer, " %d", rates[i]); - snd_iprintf(buffer, "\n"); + snd_print_pcm_rates(pcm, buf, sizeof(buf)); + snd_iprintf(buffer, "%s\n", buf); } static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm) { - static unsigned int bits[] = { 8, 16, 20, 24, 32 }; - int i; + char buf[SND_PRINT_BITS_ADVISED_BUFSIZE]; - pcm = (pcm >> 16) & 0xff; - snd_iprintf(buffer, " bits [0x%x]:", pcm); - for (i = 0; i < ARRAY_SIZE(bits); i++) - if (pcm & (1 << i)) - snd_iprintf(buffer, " %d", bits[i]); - snd_iprintf(buffer, "\n"); + snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff); + snd_print_pcm_bits(pcm, buf, sizeof(buf)); + snd_iprintf(buffer, "%s\n", buf); } static void print_pcm_formats(struct snd_info_buffer *buffer, @@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer, print_pcm_formats(buffer, stream); } -static const char *get_jack_location(u32 cfg) -{ - static char *bases[7] = { - "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom", - }; - static unsigned char specials_idx[] = { - 0x07, 0x08, - 0x17, 0x18, 0x19, - 0x37, 0x38 - }; - static char *specials[] = { - "Rear Panel", "Drive Bar", - "Riser", "HDMI", "ATAPI", - "Mobile-In", "Mobile-Out" - }; - int i; - cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; - if ((cfg & 0x0f) < 7) - return bases[cfg & 0x0f]; - for (i = 0; i < ARRAY_SIZE(specials_idx); i++) { - if (cfg == specials_idx[i]) - return specials[i]; - } - return "UNKNOWN"; -} - static const char *get_jack_connection(u32 cfg) { static char *names[16] = { @@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer, int *supports_vref) { static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; - static char *jack_types[16] = { - "Line Out", "Speaker", "HP Out", "CD", - "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", - "Line In", "Aux", "Mic", "Telephony", - "SPDIF In", "Digitial In", "Reserved", "Other" - }; - static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; unsigned int caps, val; caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); @@ -274,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer, caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], - jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT], - jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3], - get_jack_location(caps)); + snd_hda_get_jack_type(caps), + snd_hda_get_jack_connectivity(caps), + snd_hda_get_jack_location(caps)); snd_iprintf(buffer, " Conn = %s, Color = %s\n", get_jack_connection(caps), get_jack_color(caps)); @@ -457,17 +414,6 @@ static void print_conn_list(struct snd_info_buffer *buffer, } } -static void print_realtek_coef(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) -{ - int 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); -} - static void print_gpio(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { @@ -483,6 +429,8 @@ static void print_gpio(struct snd_info_buffer *buffer, (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, (gpio & AC_GPIO_WAKE) ? 1 : 0); max = gpio & AC_GPIO_IO_COUNT; + if (!max || max > 8) + return; enable = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_GPIO_MASK, 0); direction = snd_hda_codec_read(codec, nid, 0, @@ -498,12 +446,13 @@ static void print_gpio(struct snd_info_buffer *buffer, for (i = 0; i < max; ++i) snd_iprintf(buffer, " IO[%d]: enable=%d, dir=%d, wake=%d, " - "sticky=%d, data=%d\n", i, + "sticky=%d, data=%d, unsol=%d\n", i, (enable & (1<<i)) ? 1 : 0, (direction & (1<<i)) ? 1 : 0, (wake & (1<<i)) ? 1 : 0, (sticky & (1<<i)) ? 1 : 0, - (data & (1<<i)) ? 1 : 0); + (data & (1<<i)) ? 1 : 0, + (unsol & (1<<i)) ? 1 : 0); /* FIXME: add GPO and GPI pin information */ } @@ -511,12 +460,11 @@ static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hda_codec *codec = entry->private_data; - char buf[32]; hda_nid_t nid; int i, nodes; - snd_hda_get_codec_name(codec, buf, sizeof(buf)); - snd_iprintf(buffer, "Codec: %s\n", buf); + snd_iprintf(buffer, "Codec: %s\n", + codec->name ? codec->name : "Not Set"); snd_iprintf(buffer, "Address: %d\n", codec->addr); snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); @@ -545,6 +493,8 @@ static void print_codec_info(struct snd_info_entry *entry, } print_gpio(buffer, codec, codec->afg); + if (codec->proc_widget_hook) + codec->proc_widget_hook(buffer, codec, codec->afg); for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = @@ -647,9 +597,8 @@ static void print_codec_info(struct snd_info_entry *entry, if (wid_caps & AC_WCAP_PROC_WID) print_proc_caps(buffer, codec, nid); - /* NID 0x20 == Realtek Define Registers */ - if (codec->vendor_id == 0x10ec && nid == 0x20) - print_realtek_coef(buffer, codec, nid); + if (codec->proc_widget_hook) + codec->proc_widget_hook(buffer, codec, nid); } snd_hda_power_down(codec); } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2b00c4afdf97..26247cfe749d 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -27,7 +27,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" struct ad198x_spec { struct snd_kcontrol_new *mixers[5]; @@ -67,8 +66,7 @@ struct ad198x_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; + struct snd_array kctls; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = { NULL }; +static void ad198x_free_kctls(struct hda_codec *codec); + static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } + ad198x_free_kctls(codec); /* no longer needed */ return 0; } @@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec) return 0; } -static void ad198x_free(struct hda_codec *codec) +static void ad198x_free_kctls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; - unsigned int i; - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); } + snd_array_free(&spec->kctls); +} + +static void ad198x_free(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + if (!spec) + return; + + ad198x_free_kctls(codec); kfree(codec->spec); } @@ -629,6 +641,36 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "External Amplifier", + .info = ad198x_eapd_info, + .get = ad198x_eapd_get, + .put = ad198x_eapd_put, + .private_value = 0x1b | (1 << 8), /* port-D, inversed */ + }, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1986a_samsung_mixers[] = { + HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), + HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), @@ -917,6 +959,7 @@ enum { AD1986A_LAPTOP_EAPD, AD1986A_LAPTOP_AUTOMUTE, AD1986A_ULTRA, + AD1986A_SAMSUNG, AD1986A_MODELS }; @@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = { [AD1986A_LAPTOP_EAPD] = "laptop-eapd", [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", [AD1986A_ULTRA] = "ultra", + [AD1986A_SAMSUNG] = "samsung", }; static struct snd_pci_quirk ad1986a_cfg_tbl[] = { @@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG), + SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG), + SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), @@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec) break; case AD1986A_LAPTOP_EAPD: spec->mixers[0] = ad1986a_laptop_eapd_mixers; + spec->num_init_verbs = 2; + spec->init_verbs[1] = ad1986a_eapd_init_verbs; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = ad1986a_laptop_dac_nids; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1986a_laptop_eapd_capture_source; + break; + case AD1986A_SAMSUNG: + spec->mixers[0] = ad1986a_samsung_mixers; spec->num_init_verbs = 3; spec->init_verbs[1] = ad1986a_eapd_init_verbs; spec->init_verbs[2] = ad1986a_automic_verbs; @@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = { * Automatic parse of I/O pins from the BIOS configuration */ -#define NUM_CONTROL_ALLOC 32 -#define NUM_VERB_ALLOC 32 - enum { AD_CTL_WIDGET_VOL, AD_CTL_WIDGET_MUTE, @@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew; - if (spec->num_kctl_used >= spec->num_kctl_alloc) { - int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; - - knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ - if (! knew) - return -ENOMEM; - if (spec->kctl_alloc) { - memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); - kfree(spec->kctl_alloc); - } - spec->kctl_alloc = knew; - spec->num_kctl_alloc = num; - } - - knew = &spec->kctl_alloc[spec->num_kctl_used]; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; *knew = ad1988_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; knew->private_value = val; - spec->num_kctl_used++; return 0; } @@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = AD1988_SPDIF_IN; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; @@ -3860,6 +3900,10 @@ static const char *ad1884a_models[AD1884A_MODELS] = { static struct snd_pci_quirk ad1884a_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), + SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), + SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), {} }; @@ -4264,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_analog[] = { +static struct hda_codec_preset snd_hda_preset_analog[] = { { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, @@ -4282,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = { { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:11d4*"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Analog Devices HD-audio codec"); + +static struct hda_codec_preset_list analog_list = { + .preset = snd_hda_preset_analog, + .owner = THIS_MODULE, +}; + +static int __init patch_analog_init(void) +{ + return snd_hda_add_codec_preset(&analog_list); +} + +static void __exit patch_analog_exit(void) +{ + snd_hda_delete_codec_preset(&analog_list); +} + +module_init(patch_analog_init) +module_exit(patch_analog_exit) diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index ba61575983fd..233e4778bba9 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -27,7 +27,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" struct atihdmi_spec { struct hda_multi_out multiout; @@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_atihdmi[] = { - { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, - { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, +static struct hda_codec_preset snd_hda_preset_atihdmi[] = { + { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, + { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, + { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, + { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi }, - { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi }, { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:1002793c"); +MODULE_ALIAS("snd-hda-codec-id:10027919"); +MODULE_ALIAS("snd-hda-codec-id:1002791a"); +MODULE_ALIAS("snd-hda-codec-id:1002aa01"); +MODULE_ALIAS("snd-hda-codec-id:10951390"); +MODULE_ALIAS("snd-hda-codec-id:17e80047"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ATI HDMI HD-audio codec"); + +static struct hda_codec_preset_list atihdmi_list = { + .preset = snd_hda_preset_atihdmi, + .owner = THIS_MODULE, +}; + +static int __init patch_atihdmi_init(void) +{ + return snd_hda_add_codec_preset(&atihdmi_list); +} + +static void __exit patch_atihdmi_exit(void) +{ + snd_hda_delete_codec_preset(&atihdmi_list); +} + +module_init(patch_atihdmi_init) +module_exit(patch_atihdmi_exit) diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 6ef57fbfb6eb..f3ebe837f2d5 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -28,7 +28,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" #define NUM_PINS 11 @@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_cmedia[] = { +static struct hda_codec_preset snd_hda_preset_cmedia[] = { { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 }, { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:13f69880"); +MODULE_ALIAS("snd-hda-codec-id:434d4980"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("C-Media HD-audio codec"); + +static struct hda_codec_preset_list cmedia_list = { + .preset = snd_hda_preset_cmedia, + .owner = THIS_MODULE, +}; + +static int __init patch_cmedia_init(void) +{ + return snd_hda_add_codec_preset(&cmedia_list); +} + +static void __exit patch_cmedia_exit(void) +{ + snd_hda_delete_codec_preset(&cmedia_list); +} + +module_init(patch_cmedia_init) +module_exit(patch_cmedia_exit) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7c1eb23f0cec..b20e1cede00b 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -27,7 +27,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -86,8 +85,6 @@ struct conexant_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -344,15 +341,6 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { - struct conexant_spec *spec = codec->spec; - unsigned int i; - - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); - } - kfree(codec->spec); } @@ -1782,7 +1770,7 @@ static int patch_cxt5051(struct hda_codec *codec) /* */ -struct hda_codec_preset snd_hda_preset_conexant[] = { +static struct hda_codec_preset snd_hda_preset_conexant[] = { { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", @@ -1791,3 +1779,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5051 }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:14f15045"); +MODULE_ALIAS("snd-hda-codec-id:14f15047"); +MODULE_ALIAS("snd-hda-codec-id:14f15051"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Conexant HD-audio codec"); + +static struct hda_codec_preset_list conexant_list = { + .preset = snd_hda_preset_conexant, + .owner = THIS_MODULE, +}; + +static int __init patch_conexant_init(void) +{ + return snd_hda_add_codec_preset(&conexant_list); +} + +static void __exit patch_conexant_exit(void) +{ + snd_hda_delete_codec_preset(&conexant_list); +} + +module_init(patch_conexant_init) +module_exit(patch_conexant_exit) diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c new file mode 100644 index 000000000000..3564f4e4b74c --- /dev/null +++ b/sound/pci/hda/patch_intelhdmi.c @@ -0,0 +1,711 @@ +/* + * + * patch_intelhdmi.c - Patch for Intel HDMI codecs + * + * Copyright(c) 2008 Intel Corporation. All rights reserved. + * + * Authors: + * Jiang Zhe <zhe.jiang@intel.com> + * Wu Fengguang <wfg@linux.intel.com> + * + * Maintained by: + * Wu Fengguang <wfg@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +#define CVT_NID 0x02 /* audio converter */ +#define PIN_NID 0x03 /* 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; +}; + +static struct hda_verb pinout_enable_verb[] = { + {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {} /* terminator */ +}; + +static struct hda_verb pinout_disable_verb[] = { + {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, + {} +}; + +static struct hda_verb unsolicited_response_verb[] = { + {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | + INTEL_HDMI_EVENT_TAG}, + {} +}; + +static struct hda_verb def_chan_map[] = { + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66}, + {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77}, + {} +}; + + +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; +}; + +/* + * 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: 8 7 6 5 4 3 2 1 */ +{ .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 } }, +{ .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 } }, +{ .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 + */ + +#ifdef BE_PARANOID +static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid, + int *packet_index, int *byte_index) +{ + int val; + + val = snd_hda_codec_read(codec, 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, + 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); +} + +static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid, + unsigned char val) +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val); +} + +static void hdmi_enable_output(struct hda_codec *codec) +{ + /* Enable Audio InfoFrame Transmission */ + 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); + /* 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_sequence_write(codec, pinout_enable_verb); +} + +static void hdmi_disable_output(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, pinout_disable_verb); + 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_MUTE); + + /* + * FIXME: noises may arise when playing music after reloading the + * kernel module, until the next X restart or monitor repower. + */ +} + +static int hdmi_get_channel_count(struct hda_codec *codec) +{ + return 1 + snd_hda_codec_read(codec, CVT_NID, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hda_codec *codec, int chs) +{ + snd_hda_codec_write(codec, CVT_NID, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); + + if (chs != hdmi_get_channel_count(codec)) + snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n", + chs, hdmi_get_channel_count(codec)); +} + +static void hdmi_debug_channel_mapping(struct hda_codec *codec) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int slot; + + for (i = 0; i < 8; i++) { + slot = snd_hda_codec_read(codec, CVT_NID, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, i); + printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", + slot >> 4, slot & 0x7); + } +#endif +} + +static void hdmi_parse_eld(struct hda_codec *codec) +{ + struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld = &spec->sink_eld; + + if (!snd_hdmi_get_eld(eld, codec, PIN_NID)) + snd_hdmi_show_eld(eld); +} + + +/* + * Audio InfoFrame routines + */ + +static void hdmi_debug_dip_size(struct hda_codec *codec) +{ +#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) +{ +#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_fill_audio_infoframe(struct hda_codec *codec, + struct hdmi_audio_infoframe *ai) +{ + u8 *params = (u8 *)ai; + int i; + + hdmi_debug_dip_size(codec); + hdmi_clear_dip_buffers(codec); /* be paranoid */ + + 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]); +} + +/* + * 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, + struct hdmi_audio_infoframe *ai) +{ + struct intel_hdmi_spec *spec = codec->spec; + struct hdmi_eld *eld = &spec->sink_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 (!eld->eld_ver) + return 0; + if (!eld->spk_alloc) + return 0; + if (channels <= 2) + return 0; + + /* + * 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, + struct hdmi_audio_infoframe *ai) +{ + if (!ai->CA) + return; + + /* + * TODO: adjust channel mapping if necessary + * ALSA sequence is front/surr/clfe/side? + */ + + snd_hda_sequence_write(codec, def_chan_map); + hdmi_debug_channel_mapping(codec); +} + + +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_audio_infoframe ai = { + .type = 0x84, + .ver = 0x01, + .len = 0x0a, + .CC02_CT47 = substream->runtime->channels - 1, + }; + + hdmi_setup_channel_allocation(codec, &ai); + hdmi_setup_channel_mapping(codec, &ai); + + hdmi_fill_audio_infoframe(codec, &ai); +} + + +/* + * Unsolicited events + */ + +static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + int pind = !!(res & AC_UNSOL_RES_PD); + int eldv = !!(res & AC_UNSOL_RES_ELDV); + + printk(KERN_INFO + "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n", + pind, eldv); + + if (pind && eldv) { + hdmi_parse_eld(codec); + /* TODO: do real things about ELD */ + } +} + +static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res) +{ + 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", + 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) +{ + 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) { + 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 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); +} + +static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct intel_hdmi_spec *spec = codec->spec; + + hdmi_disable_output(codec); + + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +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; + + snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); + + hdmi_set_channel_count(codec, substream->runtime->channels); + + hdmi_setup_audio_infoframe(codec, substream); + + hdmi_enable_output(codec); + + return 0; +} + +static struct hda_pcm_stream intel_hdmi_pcm_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = CVT_NID, /* NID to query formats and rates and setup streams */ + .ops = { + .open = intel_hdmi_playback_pcm_open, + .close = intel_hdmi_playback_pcm_close, + .prepare = intel_hdmi_playback_pcm_prepare + }, +}; + +static int intel_hdmi_build_pcms(struct hda_codec *codec) +{ + struct intel_hdmi_spec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "INTEL HDMI"; + info->pcm_type = HDA_PCM_TYPE_HDMI; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback; + + return 0; +} + +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); + if (err < 0) + return err; + + return 0; +} + +static int intel_hdmi_init(struct hda_codec *codec) +{ + /* disable audio output as early as possible */ + hdmi_disable_output(codec); + + snd_hda_sequence_write(codec, unsolicited_response_verb); + + return 0; +} + +static void intel_hdmi_free(struct hda_codec *codec) +{ + struct intel_hdmi_spec *spec = codec->spec; + + snd_hda_eld_proc_free(codec, &spec->sink_eld); + kfree(spec); +} + +static struct hda_codec_ops intel_hdmi_patch_ops = { + .init = intel_hdmi_init, + .free = intel_hdmi_free, + .build_pcms = intel_hdmi_build_pcms, + .build_controls = intel_hdmi_build_controls, + .unsol_event = intel_hdmi_unsol_event, +}; + +static int patch_intel_hdmi(struct hda_codec *codec) +{ + struct intel_hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + 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; + + snd_hda_eld_proc_new(codec, &spec->sink_eld); + + init_channel_allocations(); + + return 0; +} + +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 = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, + {} /* terminator */ +}; + +MODULE_ALIAS("snd-hda-codec-id:808629fb"); +MODULE_ALIAS("snd-hda-codec-id:80862801"); +MODULE_ALIAS("snd-hda-codec-id:80862802"); +MODULE_ALIAS("snd-hda-codec-id:80862803"); +MODULE_ALIAS("snd-hda-codec-id:10951392"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel HDMI HD-audio codec"); + +static struct hda_codec_preset_list intel_list = { + .preset = snd_hda_preset_intelhdmi, + .owner = THIS_MODULE, +}; + +static int __init patch_intelhdmi_init(void) +{ + return snd_hda_add_codec_preset(&intel_list); +} + +static void __exit patch_intelhdmi_exit(void) +{ + snd_hda_delete_codec_preset(&intel_list); +} + +module_init(patch_intelhdmi_init) +module_exit(patch_intelhdmi_exit) diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c index 1a65775d28e1..0270fda0bda5 100644 --- a/sound/pci/hda/patch_nvhdmi.c +++ b/sound/pci/hda/patch_nvhdmi.c @@ -116,6 +116,7 @@ static int nvhdmi_build_pcms(struct hda_codec *codec) codec->pcm_info = info; info->name = "NVIDIA HDMI"; + info->pcm_type = HDA_PCM_TYPE_HDMI; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback; return 0; @@ -157,8 +158,34 @@ static int patch_nvhdmi(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_nvhdmi[] = { - { .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi }, - { .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi }, +static struct hda_codec_preset snd_hda_preset_nvhdmi[] = { + { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi }, + { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi }, + { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:10de0002"); +MODULE_ALIAS("snd-hda-codec-id:10de0007"); +MODULE_ALIAS("snd-hda-codec-id:10de0067"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec"); + +static struct hda_codec_preset_list nvhdmi_list = { + .preset = snd_hda_preset_nvhdmi, + .owner = THIS_MODULE, +}; + +static int __init patch_nvhdmi_init(void) +{ + return snd_hda_add_codec_preset(&nvhdmi_list); +} + +static void __exit patch_nvhdmi_exit(void) +{ + snd_hda_delete_codec_preset(&nvhdmi_list); +} + +module_init(patch_nvhdmi_init) +module_exit(patch_nvhdmi_exit) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0b6e682c46d0..0bd4e6bf354d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -30,7 +30,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" #define ALC880_FRONT_EVENT 0x01 #define ALC880_DCVOL_EVENT 0x02 @@ -114,6 +113,7 @@ enum { ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, + ALC268_ACER_DMIC, ALC268_ACER_ASPIRE_ONE, ALC268_DELL, ALC268_ZEPTO, @@ -130,6 +130,8 @@ enum { ALC269_QUANTA_FL1, ALC269_ASUS_EEEPC_P703, ALC269_ASUS_EEEPC_P901, + ALC269_FUJITSU, + ALC269_LIFEBOOK, ALC269_AUTO, ALC269_MODEL_LAST /* last tag */ }; @@ -152,6 +154,7 @@ enum { enum { ALC660VD_3ST, ALC660VD_3ST_DIG, + ALC660VD_ASUS_V1S, ALC861VD_3ST, ALC861VD_3ST_DIG, ALC861VD_6ST_DIG, @@ -212,6 +215,7 @@ enum { ALC883_TARGA_2ch_DIG, ALC883_ACER, ALC883_ACER_ASPIRE, + ALC888_ACER_ASPIRE_4930G, ALC883_MEDION, ALC883_MEDION_MD2, ALC883_LAPTOP_EAPD, @@ -225,9 +229,11 @@ enum { ALC883_MITAC, ALC883_CLEVO_M720, ALC883_FUJITSU_PI2515, + ALC888_FUJITSU_XA3530, ALC883_3ST_6ch_INTEL, ALC888_ASUS_M90V, ALC888_ASUS_EEE1601, + ALC1200_ASUS_P5Q, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -239,6 +245,7 @@ struct alc_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; + struct snd_kcontrol_new *cap_mixer; /* capture mixer */ const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -268,6 +275,7 @@ struct alc_spec { hda_nid_t *adc_nids; hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + unsigned char is_mix_capture; /* matrix-style capture (non-mux) */ /* capture source */ unsigned int num_mux_defs; @@ -284,8 +292,7 @@ struct alc_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; + struct snd_array kctls; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -307,6 +314,13 @@ struct alc_spec { /* for PLL fix */ hda_nid_t pll_nid; unsigned int pll_coef_idx, pll_coef_bit; + +#ifdef SND_HDA_NEEDS_RESUME +#define ALC_MAX_PINS 16 + unsigned int num_pins; + hda_nid_t pin_nids[ALC_MAX_PINS]; + unsigned int pin_cfgs[ALC_MAX_PINS]; +#endif }; /* @@ -316,6 +330,7 @@ struct alc_config_preset { struct snd_kcontrol_new *mixers[5]; /* should be identical size * with spec */ + struct snd_kcontrol_new *cap_mixer; /* capture mixer */ const struct hda_verb *init_verbs[5]; unsigned int num_dacs; hda_nid_t *dac_nids; @@ -368,14 +383,39 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + unsigned int mux_idx; hda_nid_t nid = spec->capsrc_nids ? spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; - return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - nid, &spec->cur_mux[adc_idx]); -} + mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; + imux = &spec->input_mux[mux_idx]; + + if (spec->is_mix_capture) { + /* Matrix-mixer style (e.g. ALC882) */ + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); + } + *cur_val = idx; + return 1; + } else { + /* MUX style (e.g. ALC880) */ + return snd_hda_input_mux_put(codec, imux, ucontrol, nid, + &spec->cur_mux[adc_idx]); + } +} /* * channel mode setting @@ -710,6 +750,43 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, #endif /* CONFIG_SND_DEBUG */ /* + */ +static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix) +{ + if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers))) + return; + spec->mixers[spec->num_mixers++] = mix; +} + +static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) +{ + if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs))) + return; + 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 */ static void setup_preset(struct alc_spec *spec, @@ -718,11 +795,11 @@ static void setup_preset(struct alc_spec *spec, int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) - spec->mixers[spec->num_mixers++] = preset->mixers[i]; + add_mixer(spec, preset->mixers[i]); + spec->cap_mixer = preset->cap_mixer; for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++) - spec->init_verbs[spec->num_init_verbs++] = - preset->init_verbs[i]; + add_verb(spec, preset->init_verbs[i]); spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; @@ -822,6 +899,31 @@ static void alc_sku_automute(struct hda_codec *codec) spec->jack_present ? 0 : PIN_OUT); } +#if 0 /* it's broken in some acses -- temporarily disabled */ +static void alc_mic_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; + unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; + unsigned int mix_nid = spec->capsrc_nids[0]; + unsigned int capsrc_idx_mic, capsrc_idx_fmic; + + capsrc_idx_mic = mic_nid - 0x18; + capsrc_idx_fmic = fmic_nid - 0x18; + present = snd_hda_codec_read(codec, mic_nid, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80)); + snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0)); + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} +#else +#define alc_mic_automute(codec) /* NOP */ +#endif /* disabled */ + /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -829,10 +931,17 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) res >>= 28; else res >>= 26; - if (res != ALC880_HP_EVENT) - return; + if (res == ALC880_HP_EVENT) + alc_sku_automute(codec); + if (res == ALC880_MIC_EVENT) + alc_mic_automute(codec); +} + +static void alc_inithook(struct hda_codec *codec) +{ alc_sku_automute(codec); + alc_mic_automute(codec); } /* additional initialization for ALC888 variants */ @@ -1018,10 +1127,19 @@ do_sku: else return; } - - snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - AC_USRSP_EN | ALC880_HP_EVENT); + if (spec->autocfg.hp_pins[0]) + snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_HP_EVENT); + +#if 0 /* it's broken in some acses -- temporarily disabled */ + if (spec->autocfg.input_pins[AUTO_PIN_MIC] && + spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]) + snd_hda_codec_write(codec, + spec->autocfg.input_pins[AUTO_PIN_MIC], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_MIC_EVENT); +#endif /* disabled */ spec->unsol_event = alc_sku_unsol_event; } @@ -1059,6 +1177,226 @@ static void alc_fix_pincfg(struct hda_codec *codec, } /* + * ALC888 + */ + +/* + * 2ch mode + */ +static struct hda_verb alc888_4ST_ch2_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Line in */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 4ch mode + */ +static struct hda_verb alc888_4ST_ch4_intel_init[] = { +/* Mic-in jack as mic in */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Front */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00}, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc888_4ST_ch6_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc888_4ST_ch8_intel_init[] = { +/* Mic-in jack as CLFE */ + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-in jack as Surround */ + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +/* Line-Out as Side */ + { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + { } /* end */ +}; + +static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = { + { 2, alc888_4ST_ch2_intel_init }, + { 4, alc888_4ST_ch4_intel_init }, + { 6, alc888_4ST_ch6_intel_init }, + { 8, alc888_4ST_ch8_intel_init }, +}; + +/* + * ALC888 Fujitsu Siemens Amillo xa3530 + */ + +static struct hda_verb alc888_fujitsu_xa3530_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Connect Internal HP to Front */ + {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, 0x00}, +/* Connect Bass HP to Front */ + {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, 0x00}, +/* Connect Line-Out side jack (SPDIF) to Side */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, +/* Connect Mic jack to CLFE */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, +/* Connect Line-in jack to Surround */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, +/* Connect HP out jack to Front */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, +/* Enable unsolicited event for HP jack and Line-out jack */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {} +}; + +static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int bits; + /* Line out presence */ + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + /* HP out presence */ + present = present || snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + /* Toggle internal speakers muting */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + /* Toggle internal bass muting */ + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if (res >> 26 == ALC880_HP_EVENT) + alc888_fujitsu_xa3530_automute(codec); +} + + +/* + * ALC888 Acer Aspire 4930G model + */ + +static struct hda_verb alc888_acer_aspire_4930g_verbs[] = { +/* Front Mic: set to PIN_IN (empty by default) */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +/* Unselect Front Mic by default in input mixer 3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)}, +/* Enable unsolicited event for HP jack */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +/* Connect Internal HP to front */ + {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, 0x00}, +/* Connect HP out to front */ + {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, 0x00}, + { } +}; + +static struct hda_input_mux alc888_2_capture_sources[2] = { + /* Front mic only available on one ADC */ + { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "Front Mic", 0xb }, + }, + }, + { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, + } +}; + +static struct snd_kcontrol_new alc888_base_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("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ +}; + +static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int bits; + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); +} + +static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if (res >> 26 == ALC880_HP_EVENT) + alc888_acer_aspire_4930g_automute(codec); +} + +/* * ALC880 3-stack model * * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) @@ -1157,49 +1495,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = { }; /* capture mixer elements */ -static struct snd_kcontrol_new alc880_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 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 = 3, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; +static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int err; -/* capture mixer elements (in case NID 0x07 not available) */ -static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, + HDA_INPUT); + err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int err; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, + HDA_INPUT); + err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + getput_call_t func) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int err; + + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx], + 3, 0, HDA_INPUT); + err = func(kcontrol, ucontrol); + mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */ + return err; +} + +static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_get); +} + +static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put); +} + +/* capture mixer elements */ +#define alc_cap_sw_info snd_ctl_boolean_stereo_info + +static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_get); +} + +static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return alc_cap_getput_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put); +} + +#define DEFINE_CAPMIX(num) \ +static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Switch", \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .count = num, \ + .info = alc_cap_sw_info, \ + .get = alc_cap_sw_get, \ + .put = alc_cap_sw_put, \ + }, \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Capture Volume", \ + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ + .count = num, \ + .info = alc_cap_vol_info, \ + .get = alc_cap_vol_get, \ + .put = alc_cap_vol_put, \ + .tlv = { .c = alc_cap_vol_tlv }, \ + }, \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + /* .name = "Capture Source", */ \ + .name = "Input Source", \ + .count = num, \ + .info = alc_mux_enum_info, \ + .get = alc_mux_enum_get, \ + .put = alc_mux_enum_put, \ + }, \ + { } /* end */ \ +} + +/* up to three ADCs */ +DEFINE_CAPMIX(1); +DEFINE_CAPMIX(2); +DEFINE_CAPMIX(3); /* @@ -1485,18 +1900,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -1571,6 +1974,7 @@ static const char *alc_slave_vols[] = { "Speaker Playback Volume", "Mono Playback Volume", "Line-Out Playback Volume", + "PCM Playback Volume", NULL, }; @@ -1590,6 +1994,9 @@ static const char *alc_slave_sws[] = { /* * build control elements */ + +static void alc_free_kctls(struct hda_codec *codec); + static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1601,7 +2008,11 @@ static int alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } - + if (spec->cap_mixer) { + err = snd_hda_add_new_ctls(codec, spec->cap_mixer); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -1636,6 +2047,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } + alc_free_kctls(codec); /* no longer needed */ return 0; } @@ -2726,23 +3138,89 @@ static int alc_build_pcms(struct hda_codec *codec) return 0; } +static void alc_free_kctls(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + static void alc_free(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int i; if (!spec) return; - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); - } + alc_free_kctls(codec); kfree(spec); codec->spec = NULL; /* to be sure */ } +#ifdef SND_HDA_NEEDS_RESUME +static void store_pin_configs(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wid_caps = get_wcaps(codec, nid); + unsigned int wid_type = + (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wid_type != AC_WID_PIN) + continue; + if (spec->num_pins >= ARRAY_SIZE(spec->pin_nids)) + break; + spec->pin_nids[spec->num_pins] = nid; + spec->pin_cfgs[spec->num_pins] = + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + spec->num_pins++; + } +} + +static void resume_pin_configs(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) { + hda_nid_t pin_nid = spec->pin_nids[i]; + unsigned int pin_config = spec->pin_cfgs[i]; + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, + pin_config & 0x000000ff); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, + (pin_config & 0x0000ff00) >> 8); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, + (pin_config & 0x00ff0000) >> 16); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, + pin_config >> 24); + } +} + +static int alc_resume(struct hda_codec *codec) +{ + resume_pin_configs(codec); + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + return 0; +} +#else +#define store_pin_configs(codec) +#endif + /* */ static struct hda_codec_ops alc_patch_ops = { @@ -2751,6 +3229,9 @@ static struct hda_codec_ops alc_patch_ops = { .init = alc_init, .free = alc_free, .unsol_event = alc_unsol_event, +#ifdef SND_HDA_NEEDS_RESUME + .resume = alc_resume, +#endif #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = alc_check_power_status, #endif @@ -3159,6 +3640,8 @@ static struct alc_config_preset alc880_presets[] = { alc880_gpio2_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_dac_nids), .dac_nids = alc880_dac_nids, + .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */ + .num_adc_nids = 1, /* single ADC */ .hp_nid = 0x03, .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), .channel_mode = alc880_2_jack_modes, @@ -3423,9 +3906,6 @@ static struct alc_config_preset alc880_presets[] = { * Automatic parse of I/O pins from the BIOS configuration */ -#define NUM_CONTROL_ALLOC 32 -#define NUM_VERB_ALLOC 32 - enum { ALC_CTL_WIDGET_VOL, ALC_CTL_WIDGET_MUTE, @@ -3443,29 +3923,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew; - if (spec->num_kctl_used >= spec->num_kctl_alloc) { - int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; - - /* array + terminator */ - knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); - if (!knew) - return -ENOMEM; - if (spec->kctl_alloc) { - memcpy(knew, spec->kctl_alloc, - sizeof(*knew) * spec->num_kctl_alloc); - kfree(spec->kctl_alloc); - } - spec->kctl_alloc = knew; - spec->num_kctl_alloc = num; - } - - knew = &spec->kctl_alloc[spec->num_kctl_used]; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; *knew = alc880_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); if (!knew->name) return -ENOMEM; knew->private_value = val; - spec->num_kctl_used++; return 0; } @@ -3789,14 +4255,15 @@ static int alc880_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = ALC880_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs; + add_verb(spec, alc880_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; + store_pin_configs(codec); return 1; } @@ -3808,13 +4275,24 @@ static void alc880_auto_init(struct hda_codec *codec) alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* * OK, here we have finally the patch for ALC880 */ +static void set_capture_mixer(struct alc_spec *spec) +{ + static struct snd_kcontrol_new *caps[3] = { + alc_capture_mixer1, + alc_capture_mixer2, + alc_capture_mixer3, + }; + if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) + spec->cap_mixer = caps[spec->num_adc_nids - 1]; +} + static int patch_alc880(struct hda_codec *codec) { struct alc_spec *spec; @@ -3870,16 +4348,12 @@ static int patch_alc880(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN) { spec->adc_nids = alc880_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt); - spec->mixers[spec->num_mixers] = - alc880_capture_alt_mixer; - spec->num_mixers++; } else { spec->adc_nids = alc880_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids); - spec->mixers[spec->num_mixers] = alc880_capture_mixer; - spec->num_mixers++; } } + set_capture_mixer(spec); spec->vmaster_nid = 0x0c; @@ -3890,6 +4364,7 @@ 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; } @@ -3914,11 +4389,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = { 0x05, }; -static hda_nid_t alc260_hp_adc_nids[2] = { - /* ADC1, 0 */ - 0x05, 0x04 -}; - /* NIDs used when simultaneous access to both ADCs makes sense. Note that * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC. */ @@ -4047,13 +4517,13 @@ static void alc260_hp_master_update(struct hda_codec *codec, struct alc_spec *spec = codec->spec; unsigned int val = spec->master_sw ? PIN_HP : 0; /* change HP and line-out pins */ - snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); - snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); /* mono (speaker) depending on the HP jack sense */ val = (val && !spec->jack_present) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); } @@ -4132,7 +4602,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { .info = snd_ctl_boolean_mono_info, .get = alc260_hp_master_sw_get, .put = alc260_hp_master_sw_put, - .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 + .private_value = (0x15 << 16) | (0x10 << 8) | 0x11 }, HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), @@ -4185,7 +4655,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec) present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; - alc260_hp_master_update(codec, 0x10, 0x15, 0x11); + alc260_hp_master_update(codec, 0x15, 0x10, 0x11); } static void alc260_hp_3013_unsol_event(struct hda_codec *codec, @@ -4317,45 +4787,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { { } /* end */ }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc260_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 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 = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x05, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - /* * initialization verbs */ @@ -4961,7 +5392,7 @@ static struct hda_verb alc260_test_init_verbs[] = { */ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, - const char *pfx) + const char *pfx, int *vol_bits) { hda_nid_t nid_vol; unsigned long vol_val, sw_val; @@ -4983,10 +5414,14 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid, } else return 0; /* N/A */ - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val); - if (err < 0) - return err; + 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); + 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); if (err < 0) @@ -5000,6 +5435,7 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, { hda_nid_t nid; int err; + int vols = 0; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = spec->private_dac_nids; @@ -5007,21 +5443,22 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, nid = cfg->line_out_pins[0]; if (nid) { - err = alc260_add_playback_controls(spec, nid, "Front"); + err = alc260_add_playback_controls(spec, nid, "Front", &vols); if (err < 0) return err; } nid = cfg->speaker_pins[0]; if (nid) { - err = alc260_add_playback_controls(spec, nid, "Speaker"); + err = alc260_add_playback_controls(spec, nid, "Speaker", &vols); if (err < 0) return err; } nid = cfg->hp_pins[0]; if (nid) { - err = alc260_add_playback_controls(spec, nid, "Headphone"); + err = alc260_add_playback_controls(spec, nid, "Headphone", + &vols); if (err < 0) return err; } @@ -5166,7 +5603,6 @@ static struct hda_verb alc260_volume_init_verbs[] = { static int alc260_parse_auto_config(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int wcap; int err; static hda_nid_t alc260_ignore[] = { 0x17, 0 }; @@ -5177,7 +5613,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec) err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; - if (!spec->kctl_alloc) + if (!spec->kctls.list) return 0; /* can't find valid BIOS pin config */ err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg); if (err < 0) @@ -5187,28 +5623,15 @@ static int alc260_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC260_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs; + add_verb(spec, alc260_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; - /* check whether NID 0x04 is valid */ - wcap = get_wcaps(codec, 0x04); - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */ - if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { - spec->adc_nids = alc260_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); - spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer; - } else { - spec->adc_nids = alc260_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); - spec->mixers[spec->num_mixers] = alc260_capture_mixer; - } - spec->num_mixers++; - + store_pin_configs(codec); return 1; } @@ -5219,7 +5642,7 @@ static void alc260_auto_init(struct hda_codec *codec) alc260_auto_init_multi_out(codec); alc260_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5277,12 +5700,11 @@ static struct alc_config_preset alc260_presets[] = { [ALC260_BASIC] = { .mixers = { alc260_base_output_mixer, alc260_input_mixer, - alc260_pc_beep_mixer, - alc260_capture_mixer }, + alc260_pc_beep_mixer }, .init_verbs = { alc260_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), + .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids), .adc_nids = alc260_adc_nids, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, @@ -5290,14 +5712,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP] = { .mixers = { alc260_hp_output_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_init_verbs, alc260_hp_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5306,14 +5727,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP_DC7600] = { .mixers = { alc260_hp_dc7600_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_init_verbs, alc260_hp_dc7600_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5322,14 +5742,13 @@ static struct alc_config_preset alc260_presets[] = { }, [ALC260_HP_3013] = { .mixers = { alc260_hp_3013_mixer, - alc260_input_mixer, - alc260_capture_alt_mixer }, + alc260_input_mixer }, .init_verbs = { alc260_hp_3013_init_verbs, alc260_hp_3013_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, - .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), - .adc_nids = alc260_hp_adc_nids, + .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt), + .adc_nids = alc260_adc_nids_alt, .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, @@ -5337,8 +5756,7 @@ static struct alc_config_preset alc260_presets[] = { .init_hook = alc260_hp_3013_automute, }, [ALC260_FUJITSU_S702X] = { - .mixers = { alc260_fujitsu_mixer, - alc260_capture_mixer }, + .mixers = { alc260_fujitsu_mixer }, .init_verbs = { alc260_fujitsu_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5350,8 +5768,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = alc260_fujitsu_capture_sources, }, [ALC260_ACER] = { - .mixers = { alc260_acer_mixer, - alc260_capture_mixer }, + .mixers = { alc260_acer_mixer }, .init_verbs = { alc260_acer_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5363,8 +5780,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = alc260_acer_capture_sources, }, [ALC260_WILL] = { - .mixers = { alc260_will_mixer, - alc260_capture_mixer }, + .mixers = { alc260_will_mixer }, .init_verbs = { alc260_init_verbs, alc260_will_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5376,8 +5792,7 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = &alc260_capture_source, }, [ALC260_REPLACER_672V] = { - .mixers = { alc260_replacer_672v_mixer, - alc260_capture_mixer }, + .mixers = { alc260_replacer_672v_mixer }, .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, @@ -5392,8 +5807,7 @@ static struct alc_config_preset alc260_presets[] = { }, #ifdef CONFIG_SND_DEBUG [ALC260_TEST] = { - .mixers = { alc260_test_mixer, - alc260_capture_mixer }, + .mixers = { alc260_test_mixer }, .init_verbs = { alc260_test_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_test_dac_nids), .dac_nids = alc260_test_dac_nids, @@ -5452,6 +5866,21 @@ static int patch_alc260(struct hda_codec *codec) spec->stream_digital_playback = &alc260_pcm_digital_playback; spec->stream_digital_capture = &alc260_pcm_digital_capture; + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x04 is valid */ + unsigned int wcap = get_wcaps(codec, 0x04); + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + /* get type */ + if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { + spec->adc_nids = alc260_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt); + } else { + spec->adc_nids = alc260_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids); + } + } + set_capture_mixer(spec); + spec->vmaster_nid = 0x08; codec->patch_ops = alc_patch_ops; @@ -5461,6 +5890,7 @@ 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; } @@ -5508,36 +5938,6 @@ static struct hda_input_mux alc882_capture_source = { { "CD", 0x4 }, }, }; -#define alc882_mux_enum_info alc_mux_enum_info -#define alc882_mux_enum_get alc_mux_enum_get - -static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - hda_nid_t nid = spec->capsrc_nids ? - spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - } - *cur_val = idx; - return 1; -} - /* * 2ch mode */ @@ -6220,49 +6620,6 @@ static struct hda_verb alc882_auto_init_verbs[] = { { } }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc882_capture_alt_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, - { } /* end */ -}; - -static struct snd_kcontrol_new alc882_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 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 = 3, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, - { } /* end */ -}; - #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc882_loopbacks alc880_loopbacks #endif @@ -6391,8 +6748,7 @@ static struct alc_config_preset alc882_presets[] = { .init_hook = alc885_imac24_init_hook, }, [ALC882_TARGA] = { - .mixers = { alc882_targa_mixer, alc882_chmode_mixer, - alc882_capture_mixer }, + .mixers = { alc882_targa_mixer, alc882_chmode_mixer }, .init_verbs = { alc882_init_verbs, alc882_targa_verbs}, .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, @@ -6408,8 +6764,7 @@ static struct alc_config_preset alc882_presets[] = { .init_hook = alc882_targa_automute, }, [ALC882_ASUS_A7J] = { - .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer, - alc882_capture_mixer }, + .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer }, .init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs}, .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, @@ -6629,7 +6984,7 @@ static void alc882_auto_init(struct hda_codec *codec) alc882_auto_init_analog_input(codec); alc882_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ @@ -6714,6 +7069,7 @@ static int patch_alc882(struct hda_codec *codec) spec->stream_digital_playback = &alc882_pcm_digital_playback; spec->stream_digital_capture = &alc882_pcm_digital_capture; + spec->is_mix_capture = 1; /* matrix-style capture */ if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); @@ -6723,17 +7079,13 @@ static int patch_alc882(struct hda_codec *codec) spec->adc_nids = alc882_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt); spec->capsrc_nids = alc882_capsrc_nids_alt; - spec->mixers[spec->num_mixers] = - alc882_capture_alt_mixer; - spec->num_mixers++; } else { spec->adc_nids = alc882_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids); spec->capsrc_nids = alc882_capsrc_nids; - spec->mixers[spec->num_mixers] = alc882_capture_mixer; - spec->num_mixers++; } } + set_capture_mixer(spec); spec->vmaster_nid = 0x0c; @@ -6744,6 +7096,7 @@ 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; } @@ -6762,6 +7115,8 @@ static int patch_alc882(struct hda_codec *codec) #define ALC883_DIGOUT_NID 0x06 #define ALC883_DIGIN_NID 0x0a +#define ALC1200_DIGOUT_NID 0x10 + static hda_nid_t alc883_dac_nids[4] = { /* front, rear, clfe, rear_surr */ 0x02, 0x03, 0x04, 0x05 @@ -6772,8 +7127,20 @@ static hda_nid_t alc883_adc_nids[2] = { 0x08, 0x09, }; +static hda_nid_t alc883_adc_nids_alt[1] = { + /* ADC1 */ + 0x08, +}; + +static hda_nid_t alc883_adc_nids_rev[2] = { + /* ADC2-1 */ + 0x09, 0x08 +}; + static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 }; +static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 }; + /* input MUX */ /* FIXME: should be a matrix-type input source selection */ @@ -6840,11 +7207,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = { }, }; -#define alc883_mux_enum_info alc_mux_enum_info -#define alc883_mux_enum_get alc_mux_enum_get -/* ALC883 has the ALC882-type input selection */ -#define alc883_mux_enum_put alc882_mux_enum_put - /* * 2ch mode */ @@ -6998,19 +7360,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7028,19 +7377,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = { HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7055,19 +7391,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = { HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7082,19 +7405,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = { HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7114,19 +7424,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7152,17 +7449,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7189,19 +7475,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7227,18 +7500,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7259,19 +7520,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7287,19 +7535,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7312,17 +7547,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7336,19 +7560,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7362,19 +7573,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, 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("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7387,19 +7585,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7427,19 +7612,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, - }, { } /* end */ }; @@ -7470,6 +7642,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = { HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), { @@ -7477,9 +7653,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { /* .name = "Capture Source", */ .name = "Input Source", .count = 1, - .info = alc883_mux_enum_info, - .get = alc883_mux_enum_get, - .put = alc883_mux_enum_put, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, }, { } /* end */ }; @@ -8134,27 +8310,6 @@ static struct hda_verb alc883_auto_init_verbs[] = { { } }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc883_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, - { } /* end */ -}; - static struct hda_verb alc888_asus_m90v_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -8277,6 +8432,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", [ALC883_ACER] = "acer", [ALC883_ACER_ASPIRE] = "acer-aspire", + [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g", [ALC883_MEDION] = "medion", [ALC883_MEDION_MD2] = "medion-md2", [ALC883_LAPTOP_EAPD] = "laptop-eapd", @@ -8290,24 +8446,30 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_MITAC] = "mitac", [ALC883_CLEVO_M720] = "clevo-m720", [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515", + [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530", [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel", + [ALC1200_ASUS_P5Q] = "asus-p5q", [ALC883_AUTO] = "auto", }; static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G", + ALC888_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */ SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x1043, 0x8317, "Asus M90V", ALC888_ASUS_M90V), + SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601), SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), @@ -8334,6 +8496,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), @@ -8345,12 +8508,15 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515), + SND_PCI_QUIRK(0x1734, 0x113d, "Fujitsu AMILO Xa3530", + ALC888_FUJITSU_XA3530), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY), SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2), + SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG), SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL), @@ -8434,6 +8600,8 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_tagra_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), .dig_out_nid = ALC883_DIGOUT_NID, .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, @@ -8467,6 +8635,26 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc883_acer_aspire_unsol_event, .init_hook = alc883_acer_aspire_automute, }, + [ALC888_ACER_ASPIRE_4930G] = { + .mixers = { alc888_base_mixer, + alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs, + alc888_acer_aspire_4930g_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .need_dac_fix = 1, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc888_acer_aspire_4930g_unsol_event, + .init_hook = alc888_acer_aspire_4930g_automute, + }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, alc883_chmode_mixer }, @@ -8474,6 +8662,8 @@ static struct alc_config_preset alc883_presets[] = { alc883_medion_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), .channel_mode = alc883_sixstack_modes, .input_mux = &alc883_capture_source, @@ -8516,6 +8706,8 @@ static struct alc_config_preset alc883_presets[] = { .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, + .adc_nids = alc883_adc_nids_alt, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt), .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_lenovo_101e_capture_source, @@ -8606,14 +8798,30 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event, .init_hook = alc883_2ch_fujitsu_pi2515_automute, }, + [ALC888_FUJITSU_XA3530] = { + .mixers = { alc888_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, + alc888_fujitsu_xa3530_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev), + .adc_nids = alc883_adc_nids_rev, + .capsrc_nids = alc883_capsrc_nids_rev, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes), + .channel_mode = alc888_4ST_8ch_intel_modes, + .num_mux_defs = + ARRAY_SIZE(alc888_2_capture_sources), + .input_mux = alc888_2_capture_sources, + .unsol_event = alc888_fujitsu_xa3530_unsol_event, + .init_hook = alc888_fujitsu_xa3530_automute, + }, [ALC888_LENOVO_SKY] = { .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_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_sixstack_modes), .channel_mode = alc883_sixstack_modes, .need_dac_fix = 1, @@ -8637,6 +8845,7 @@ static struct alc_config_preset alc883_presets[] = { }, [ALC888_ASUS_EEE1601] = { .mixers = { alc883_asus_eee1601_mixer }, + .cap_mixer = alc883_asus_eee1601_cap_mixer, .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, @@ -8649,6 +8858,17 @@ static struct alc_config_preset alc883_presets[] = { .unsol_event = alc883_eee1601_unsol_event, .init_hook = alc883_eee1601_inithook, }, + [ALC1200_ASUS_P5Q] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC1200_DIGOUT_NID, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, }; @@ -8743,8 +8963,6 @@ static int alc883_parse_auto_config(struct hda_codec *codec) /* hack - override the init verbs */ spec->init_verbs[0] = alc883_auto_init_verbs; - spec->mixers[spec->num_mixers] = alc883_capture_mixer; - spec->num_mixers++; return 1; /* config found */ } @@ -8758,7 +8976,7 @@ static void alc883_auto_init(struct hda_codec *codec) alc883_auto_init_analog_input(codec); alc883_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc883(struct hda_codec *codec) @@ -8802,8 +9020,13 @@ static int patch_alc883(struct hda_codec *codec) switch (codec->vendor_id) { case 0x10ec0888: - spec->stream_name_analog = "ALC888 Analog"; - spec->stream_name_digital = "ALC888 Digital"; + if (codec->revision_id == 0x100101) { + spec->stream_name_analog = "ALC1200 Analog"; + spec->stream_name_digital = "ALC1200 Digital"; + } else { + spec->stream_name_analog = "ALC888 Analog"; + spec->stream_name_digital = "ALC888 Digital"; + } break; case 0x10ec0889: spec->stream_name_analog = "ALC889 Analog"; @@ -8822,9 +9045,15 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_digital_playback = &alc883_pcm_digital_playback; spec->stream_digital_capture = &alc883_pcm_digital_capture; - spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); - spec->adc_nids = alc883_adc_nids; - spec->capsrc_nids = alc883_capsrc_nids; + if (!spec->num_adc_nids) { + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + spec->adc_nids = alc883_adc_nids; + } + if (!spec->capsrc_nids) + spec->capsrc_nids = alc883_capsrc_nids; + spec->is_mix_capture = 1; /* matrix-style capture */ + if (!spec->cap_mixer) + set_capture_mixer(spec); spec->vmaster_nid = 0x0c; @@ -8836,6 +9065,7 @@ static int patch_alc883(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc883_loopbacks; #endif + codec->proc_widget_hook = print_realtek_coef; return 0; } @@ -9315,20 +9545,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -9845,7 +10061,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol, struct alc_spec *spec = codec->spec; int ret; - ret = alc882_mux_enum_put(kcontrol, ucontrol); + ret = alc_mux_enum_put(kcontrol, ucontrol); if (!ret) return 0; /* reprogram the HP pin as mic or HP according to the input source */ @@ -9862,8 +10078,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Source", - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, .put = alc262_ultra_mux_enum_put, }, { } /* end */ @@ -10256,10 +10472,10 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = ALC262_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs; + add_verb(spec, alc262_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -10267,6 +10483,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + store_pin_configs(codec); return 1; } @@ -10285,7 +10502,7 @@ static void alc262_auto_init(struct hda_codec *codec) alc262_auto_init_analog_input(codec); alc262_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -10341,9 +10558,11 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN", + ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", ALC262_TOSHIBA_RX1), - SND_PCI_QUIRK(0x1179, 0x0268, "Toshiba S06", ALC262_TOSHIBA_S06), + SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU), SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), @@ -10499,7 +10718,8 @@ static struct alc_config_preset alc262_presets[] = { .init_hook = alc262_hippo_automute, }, [ALC262_ULTRA] = { - .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer }, + .mixers = { alc262_ultra_mixer }, + .cap_mixer = alc262_ultra_capture_mixer, .init_verbs = { alc262_ultra_verbs }, .num_dacs = ARRAY_SIZE(alc262_dac_nids), .dac_nids = alc262_dac_nids, @@ -10625,6 +10845,7 @@ static int patch_alc262(struct hda_codec *codec) spec->stream_digital_playback = &alc262_pcm_digital_playback; spec->stream_digital_capture = &alc262_pcm_digital_capture; + spec->is_mix_capture = 1; if (!spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); @@ -10635,17 +10856,14 @@ static int patch_alc262(struct hda_codec *codec) spec->adc_nids = alc262_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); spec->capsrc_nids = alc262_capsrc_nids_alt; - spec->mixers[spec->num_mixers] = - alc262_capture_alt_mixer; - spec->num_mixers++; } else { spec->adc_nids = alc262_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); spec->capsrc_nids = alc262_capsrc_nids; - spec->mixers[spec->num_mixers] = alc262_capture_mixer; - spec->num_mixers++; } } + if (!spec->cap_mixer) + set_capture_mixer(spec); spec->vmaster_nid = 0x0c; @@ -10656,6 +10874,7 @@ 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; } @@ -10817,6 +11036,22 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = { { } }; +static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT), + { } +}; + static struct hda_verb alc268_acer_aspire_one_verbs[] = { {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, @@ -11093,10 +11328,6 @@ static struct hda_verb alc268_volume_init_verbs[] = { { } }; -#define alc268_mux_enum_info alc_mux_enum_info -#define alc268_mux_enum_get alc_mux_enum_get -#define alc268_mux_enum_put alc_mux_enum_put - static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), @@ -11108,9 +11339,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { /* .name = "Capture Source", */ .name = "Input Source", .count = 1, - .info = alc268_mux_enum_info, - .get = alc268_mux_enum_get, - .put = alc268_mux_enum_put, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, }, { } /* end */ }; @@ -11128,9 +11359,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = { /* .name = "Capture Source", */ .name = "Input Source", .count = 2, - .info = alc268_mux_enum_info, - .get = alc268_mux_enum_get, - .put = alc268_mux_enum_put, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, }, { } /* end */ }; @@ -11149,6 +11380,15 @@ static struct hda_input_mux alc268_acer_capture_source = { .num_items = 3, .items = { { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Line", 0x2 }, + }, +}; + +static struct hda_input_mux alc268_acer_dmic_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, { "Internal Mic", 0x6 }, { "Line", 0x2 }, }, @@ -11387,13 +11627,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC268_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); if (spec->autocfg.speaker_pins[0] != 0x1d) - spec->mixers[spec->num_mixers++] = alc268_beep_mixer; + add_mixer(spec, alc268_beep_mixer); - spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; + add_verb(spec, alc268_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -11401,6 +11641,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + store_pin_configs(codec); return 1; } @@ -11417,7 +11658,7 @@ static void alc268_auto_init(struct hda_codec *codec) alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -11428,6 +11669,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { [ALC268_3ST] = "3stack", [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", + [ALC268_ACER_DMIC] = "acer-dmic", [ALC268_ACER_ASPIRE_ONE] = "acer-aspire", [ALC268_DELL] = "dell", [ALC268_ZEPTO] = "zepto", @@ -11523,6 +11765,23 @@ static struct alc_config_preset alc268_presets[] = { .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, + [ALC268_ACER_DMIC] = { + .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, + alc268_beep_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .capsrc_nids = alc268_capsrc_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_acer_dmic_capture_source, + .unsol_event = alc268_acer_unsol_event, + .init_hook = alc268_acer_init_hook, + }, [ALC268_ACER_ASPIRE_ONE] = { .mixers = { alc268_acer_aspire_one_mixer, alc268_capture_alt_mixer }, @@ -11661,15 +11920,11 @@ static int patch_alc268(struct hda_codec *codec) if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) { spec->adc_nids = alc268_adc_nids_alt; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); - spec->mixers[spec->num_mixers] = - alc268_capture_alt_mixer; - spec->num_mixers++; + add_mixer(spec, alc268_capture_alt_mixer); } else { spec->adc_nids = alc268_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); - spec->mixers[spec->num_mixers] = - alc268_capture_mixer; - spec->num_mixers++; + add_mixer(spec, alc268_capture_mixer); } spec->capsrc_nids = alc268_capsrc_nids; /* set default input source */ @@ -11685,6 +11940,8 @@ 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; } @@ -11767,6 +12024,31 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { { } }; +static struct snd_kcontrol_new alc269_lifebook_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT), + { } +}; + /* bind volumes of both NID 0x0c and 0x0d */ static struct hda_bind_ctls alc269_epc_bind_vol = { .ops = &snd_hda_bind_vol, @@ -11785,28 +12067,18 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = { }; /* capture mixer elements */ -static struct snd_kcontrol_new alc269_capture_mixer[] = { +static struct snd_kcontrol_new alc269_epc_capture_mixer[] = { HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x08, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), { } /* end */ }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc269_epc_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +/* FSC amilo */ +static struct snd_kcontrol_new alc269_fujitsu_mixer[] = { + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_BIND_VOL("PCM Playback Volume", &alc269_epc_bind_vol), { } /* end */ }; @@ -11827,6 +12099,20 @@ static struct hda_verb alc269_quanta_fl1_verbs[] = { { } }; +static struct hda_verb alc269_lifebook_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + { } +}; + /* toggle speaker-output according to the hp-jack state */ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) { @@ -11852,6 +12138,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) AC_VERB_SET_PROC_COEF, 0x480); } +/* toggle speaker-output according to the hp-jacks state */ +static void alc269_lifebook_speaker_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + /* Check laptop headphone socket */ + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + /* Check port replicator headphone socket */ + present |= snd_hda_codec_read(codec, 0x1a, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + bits = present ? AMP_IN_MUTE(0) : 0; + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, + AMP_IN_MUTE(0), bits); + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, + AMP_IN_MUTE(0), bits); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x680); + + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_COEF_INDEX, 0x0c); + snd_hda_codec_write(codec, 0x20, 0, + AC_VERB_SET_PROC_COEF, 0x480); +} + static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) { unsigned int present; @@ -11862,6 +12179,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1); } +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; + + /* Laptop mic port overrides dock mic port, design decision */ + if (present_dock) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x3); + if (present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x0); + if (!present_dock && !present_laptop) + snd_hda_codec_write(codec, 0x23, 0, + AC_VERB_SET_CONNECT_SEL, 0x1); +} + static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -11871,12 +12211,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, alc269_quanta_fl1_mic_automute(codec); } +static void alc269_lifebook_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc269_lifebook_speaker_automute(codec); + if ((res >> 26) == ALC880_MIC_EVENT) + alc269_lifebook_mic_autoswitch(codec); +} + static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) { alc269_quanta_fl1_speaker_automute(codec); alc269_quanta_fl1_mic_automute(codec); } +static void alc269_lifebook_init_hook(struct hda_codec *codec) +{ + alc269_lifebook_speaker_automute(codec); + alc269_lifebook_mic_autoswitch(codec); +} + static struct hda_verb alc269_eeepc_dmic_init_verbs[] = { {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, {0x23, AC_VERB_SET_CONNECT_SEL, 0x05}, @@ -12120,8 +12475,26 @@ static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec, return 0; } -#define alc269_auto_create_analog_input_ctls \ - alc880_auto_create_analog_input_ctls +static int alc269_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int err; + + err = alc880_auto_create_analog_input_ctls(spec, cfg); + if (err < 0) + return err; + /* digital-mic input pin is excluded in alc880_auto_create..() + * because it's under 0x18 + */ + if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 || + cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) { + struct hda_input_mux *imux = &spec->private_imux; + imux->items[imux->num_items].label = "Int Mic"; + imux->items[imux->num_items].index = 0x05; + imux->num_items++; + } + return 0; +} #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc269_loopbacks alc880_loopbacks @@ -12159,17 +12532,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC269_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); /* create a beep mixer control if the pin 0x1d isn't assigned */ for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++) if (spec->autocfg.input_pins[i] == 0x1d) break; if (i >= ARRAY_SIZE(spec->autocfg.input_pins)) - spec->mixers[spec->num_mixers++] = alc269_beep_mixer; + add_mixer(spec, alc269_beep_mixer); - spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs; + add_verb(spec, alc269_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; /* set default input source */ @@ -12181,9 +12554,10 @@ static int alc269_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - spec->mixers[spec->num_mixers] = alc269_capture_mixer; - spec->num_mixers++; + if (!spec->cap_mixer) + set_capture_mixer(spec); + store_pin_configs(codec); return 1; } @@ -12200,7 +12574,7 @@ static void alc269_auto_init(struct hda_codec *codec) alc269_auto_init_hp_out(codec); alc269_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -12210,7 +12584,9 @@ 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_EEEPC_P901] = "eeepc-p901", + [ALC269_FUJITSU] = "fujitsu", + [ALC269_LIFEBOOK] = "lifebook" }; static struct snd_pci_quirk alc269_cfg_tbl[] = { @@ -12221,12 +12597,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = { ALC269_ASUS_EEEPC_P901), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", ALC269_ASUS_EEEPC_P901), + SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU), + SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), {} }; static struct alc_config_preset alc269_presets[] = { [ALC269_BASIC] = { - .mixers = { alc269_base_mixer, alc269_capture_mixer }, + .mixers = { alc269_base_mixer }, .init_verbs = { alc269_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), .dac_nids = alc269_dac_nids, @@ -12248,7 +12626,8 @@ static struct alc_config_preset alc269_presets[] = { .init_hook = alc269_quanta_fl1_init_hook, }, [ALC269_ASUS_EEEPC_P703] = { - .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer }, + .mixers = { alc269_eeepc_mixer }, + .cap_mixer = alc269_epc_capture_mixer, .init_verbs = { alc269_init_verbs, alc269_eeepc_amic_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), @@ -12261,7 +12640,8 @@ static struct alc_config_preset alc269_presets[] = { .init_hook = alc269_eeepc_amic_inithook, }, [ALC269_ASUS_EEEPC_P901] = { - .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer}, + .mixers = { alc269_eeepc_mixer }, + .cap_mixer = alc269_epc_capture_mixer, .init_verbs = { alc269_init_verbs, alc269_eeepc_dmic_init_verbs }, .num_dacs = ARRAY_SIZE(alc269_dac_nids), @@ -12273,6 +12653,32 @@ static struct alc_config_preset alc269_presets[] = { .unsol_event = alc269_eeepc_dmic_unsol_event, .init_hook = alc269_eeepc_dmic_inithook, }, + [ALC269_FUJITSU] = { + .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer }, + .cap_mixer = alc269_epc_capture_mixer, + .init_verbs = { alc269_init_verbs, + alc269_eeepc_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, + .input_mux = &alc269_eeepc_dmic_capture_source, + .unsol_event = alc269_eeepc_dmic_unsol_event, + .init_hook = alc269_eeepc_dmic_inithook, + }, + [ALC269_LIFEBOOK] = { + .mixers = { alc269_lifebook_mixer }, + .init_verbs = { alc269_init_verbs, alc269_lifebook_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, + .input_mux = &alc269_capture_source, + .unsol_event = alc269_lifebook_unsol_event, + .init_hook = alc269_lifebook_init_hook, + }, }; static int patch_alc269(struct hda_codec *codec) @@ -12327,6 +12733,8 @@ static int patch_alc269(struct hda_codec *codec) spec->adc_nids = alc269_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); spec->capsrc_nids = alc269_capsrc_nids; + if (!spec->cap_mixer) + set_capture_mixer(spec); codec->patch_ops = alc_patch_ops; if (board_config == ALC269_AUTO) @@ -12335,6 +12743,7 @@ 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; } @@ -12467,17 +12876,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -12501,17 +12899,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channel Mode", @@ -12529,18 +12916,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = { HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), - /*Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ }; @@ -12564,17 +12939,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT), - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channel Mode", @@ -12606,17 +12970,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT), - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channel Mode", @@ -13148,25 +13501,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec, return 0; } -static struct snd_kcontrol_new alc861_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 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 = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, - { } /* end */ -}; - static void alc861_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, int dac_idx) @@ -13257,19 +13591,19 @@ static int alc861_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC861_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs; + add_verb(spec, alc861_auto_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; spec->adc_nids = alc861_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids); - spec->mixers[spec->num_mixers] = alc861_capture_mixer; - spec->num_mixers++; + set_capture_mixer(spec); + store_pin_configs(codec); return 1; } @@ -13281,7 +13615,7 @@ static void alc861_auto_init(struct hda_codec *codec) alc861_auto_init_hp_out(codec); alc861_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -13498,6 +13832,7 @@ 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; } @@ -13563,11 +13898,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = { }, }; -#define alc861vd_mux_enum_info alc_mux_enum_info -#define alc861vd_mux_enum_get alc_mux_enum_get -/* ALC861VD has the ALC882-type input selection (but has only one ADC) */ -#define alc861vd_mux_enum_put alc882_mux_enum_put - /* * 2ch mode */ @@ -13613,25 +13943,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = { { } /* end */ }; -static struct snd_kcontrol_new alc861vd_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 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 = 1, - .info = alc861vd_mux_enum_info, - .get = alc861vd_mux_enum_get, - .put = alc861vd_mux_enum_put, - }, - { } /* end */ -}; - /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b */ @@ -14023,6 +14334,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { [ALC660VD_3ST] = "3stack-660", [ALC660VD_3ST_DIG] = "3stack-660-digout", + [ALC660VD_ASUS_V1S] = "asus-v1s", [ALC861VD_3ST] = "3stack", [ALC861VD_3ST_DIG] = "3stack-digout", [ALC861VD_6ST_DIG] = "6stack-digout", @@ -14037,7 +14349,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), - SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), @@ -14144,6 +14456,21 @@ static struct alc_config_preset alc861vd_presets[] = { .unsol_event = alc861vd_dallas_unsol_event, .init_hook = alc861vd_dallas_automute, }, + [ALC660VD_ASUS_V1S] = { + .mixers = { alc861vd_lenovo_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs, + alc861vd_eapd_verbs, + alc861vd_lenovo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + .unsol_event = alc861vd_lenovo_unsol_event, + .init_hook = alc861vd_lenovo_automute, + }, }; /* @@ -14368,11 +14695,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); - spec->init_verbs[spec->num_init_verbs++] - = alc861vd_volume_init_verbs; + add_verb(spec, alc861vd_volume_init_verbs); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; @@ -14381,6 +14707,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + store_pin_configs(codec); return 1; } @@ -14393,7 +14720,7 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc861vd_auto_init_analog_input(codec); alc861vd_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc861vd(struct hda_codec *codec) @@ -14438,7 +14765,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->stream_name_analog = "ALC660-VD Analog"; spec->stream_name_digital = "ALC660-VD Digital"; /* always turn on EAPD */ - spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs; + add_verb(spec, alc660vd_eapd_verbs); } else { spec->stream_name_analog = "ALC861VD Analog"; spec->stream_name_digital = "ALC861VD Digital"; @@ -14453,9 +14780,9 @@ static int patch_alc861vd(struct hda_codec *codec) spec->adc_nids = alc861vd_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids); spec->capsrc_nids = alc861vd_capsrc_nids; + spec->is_mix_capture = 1; - spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; - spec->num_mixers++; + set_capture_mixer(spec); spec->vmaster_nid = 0x02; @@ -14467,6 +14794,7 @@ 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; } @@ -14542,10 +14870,6 @@ static struct hda_input_mux alc663_m51va_capture_source = { }, }; -#define alc662_mux_enum_info alc_mux_enum_info -#define alc662_mux_enum_get alc_mux_enum_get -#define alc662_mux_enum_put alc882_mux_enum_put - /* * 2ch mode */ @@ -15131,25 +15455,6 @@ static struct hda_verb alc662_ecs_init_verbs[] = { {} }; -/* capture mixer elements */ -static struct snd_kcontrol_new alc662_capture_mixer[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 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 = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, - { } /* end */ -}; - 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), @@ -15667,7 +15972,7 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA), - SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS M51VA", ALC663_ASUS_G50V), + SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V), SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), @@ -15680,6 +15985,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), + SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1), SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), @@ -15720,7 +16026,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { static struct alc_config_preset alc662_presets[] = { [ALC662_3ST_2ch_DIG] = { - .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer }, + .mixers = { alc662_3ST_2ch_mixer }, .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15731,8 +16037,7 @@ static struct alc_config_preset alc662_presets[] = { .input_mux = &alc662_capture_source, }, [ALC662_3ST_6ch_DIG] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, - alc662_capture_mixer }, + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15744,8 +16049,7 @@ static struct alc_config_preset alc662_presets[] = { .input_mux = &alc662_capture_source, }, [ALC662_3ST_6ch] = { - .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer, - alc662_capture_mixer }, + .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15755,8 +16059,7 @@ static struct alc_config_preset alc662_presets[] = { .input_mux = &alc662_capture_source, }, [ALC662_5ST_DIG] = { - .mixers = { alc662_base_mixer, alc662_chmode_mixer, - alc662_capture_mixer }, + .mixers = { alc662_base_mixer, alc662_chmode_mixer }, .init_verbs = { alc662_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15767,7 +16070,7 @@ static struct alc_config_preset alc662_presets[] = { .input_mux = &alc662_capture_source, }, [ALC662_LENOVO_101E] = { - .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer }, + .mixers = { alc662_lenovo_101e_mixer }, .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15778,7 +16081,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc662_lenovo_101e_all_automute, }, [ALC662_ASUS_EEEPC_P701] = { - .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer }, + .mixers = { alc662_eeepc_p701_mixer }, .init_verbs = { alc662_init_verbs, alc662_eeepc_sue_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15790,7 +16093,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc662_eeepc_inithook, }, [ALC662_ASUS_EEEPC_EP20] = { - .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer, + .mixers = { alc662_eeepc_ep20_mixer, alc662_chmode_mixer }, .init_verbs = { alc662_init_verbs, alc662_eeepc_ep20_sue_init_verbs }, @@ -15803,7 +16106,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc662_eeepc_ep20_inithook, }, [ALC662_ECS] = { - .mixers = { alc662_ecs_mixer, alc662_capture_mixer }, + .mixers = { alc662_ecs_mixer }, .init_verbs = { alc662_init_verbs, alc662_ecs_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15815,7 +16118,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc662_eeepc_inithook, }, [ALC663_ASUS_M51VA] = { - .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, + .mixers = { alc663_m51va_mixer }, .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15827,7 +16130,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_m51va_inithook, }, [ALC663_ASUS_G71V] = { - .mixers = { alc663_g71v_mixer, alc662_capture_mixer}, + .mixers = { alc663_g71v_mixer }, .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15839,7 +16142,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_g71v_inithook, }, [ALC663_ASUS_H13] = { - .mixers = { alc663_m51va_mixer, alc662_capture_mixer}, + .mixers = { alc663_m51va_mixer }, .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15850,7 +16153,7 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_m51va_inithook, }, [ALC663_ASUS_G50V] = { - .mixers = { alc663_g50v_mixer, alc662_capture_mixer}, + .mixers = { alc663_g50v_mixer }, .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), .dac_nids = alc662_dac_nids, @@ -15862,7 +16165,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_g50v_inithook, }, [ALC663_ASUS_MODE1] = { - .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer }, + .mixers = { alc663_m51va_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc663_21jd_amic_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15876,7 +16180,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_mode1_inithook, }, [ALC662_ASUS_MODE2] = { - .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer }, + .mixers = { alc662_1bjd_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc662_1bjd_amic_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15889,7 +16194,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc662_mode2_inithook, }, [ALC663_ASUS_MODE3] = { - .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer }, + .mixers = { alc663_two_hp_m1_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc663_two_hp_amic_m1_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15903,8 +16209,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_mode3_inithook, }, [ALC663_ASUS_MODE4] = { - .mixers = { alc663_asus_21jd_clfe_mixer, - alc662_auto_capture_mixer}, + .mixers = { alc663_asus_21jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc663_21jd_amic_init_verbs}, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15918,8 +16224,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_mode4_inithook, }, [ALC663_ASUS_MODE5] = { - .mixers = { alc663_asus_15jd_clfe_mixer, - alc662_auto_capture_mixer }, + .mixers = { alc663_asus_15jd_clfe_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc663_15jd_amic_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -15933,7 +16239,8 @@ static struct alc_config_preset alc662_presets[] = { .init_hook = alc663_mode5_inithook, }, [ALC663_ASUS_MODE6] = { - .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer }, + .mixers = { alc663_two_hp_m2_mixer }, + .cap_mixer = alc662_auto_capture_mixer, .init_verbs = { alc662_init_verbs, alc663_two_hp_amic_m2_init_verbs }, .num_dacs = ARRAY_SIZE(alc662_dac_nids), @@ -16194,23 +16501,21 @@ static int alc662_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + add_mixer(spec, spec->kctls.list); spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; - spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs; + add_verb(spec, alc662_auto_init_verbs); if (codec->vendor_id == 0x10ec0663) - spec->init_verbs[spec->num_init_verbs++] = - alc663_auto_init_verbs; + add_verb(spec, alc663_auto_init_verbs); err = alc_auto_add_mic_boost(codec); if (err < 0) return err; - spec->mixers[spec->num_mixers] = alc662_capture_mixer; - spec->num_mixers++; + store_pin_configs(codec); return 1; } @@ -16223,7 +16528,7 @@ static void alc662_auto_init(struct hda_codec *codec) alc662_auto_init_analog_input(codec); alc662_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc662(struct hda_codec *codec) @@ -16268,6 +16573,9 @@ static int patch_alc662(struct hda_codec *codec) if (codec->vendor_id == 0x10ec0663) { spec->stream_name_analog = "ALC663 Analog"; spec->stream_name_digital = "ALC663 Digital"; + } else if (codec->vendor_id == 0x10ec0272) { + spec->stream_name_analog = "ALC272 Analog"; + spec->stream_name_digital = "ALC272 Digital"; } else { spec->stream_name_analog = "ALC662 Analog"; spec->stream_name_digital = "ALC662 Digital"; @@ -16282,6 +16590,10 @@ static int patch_alc662(struct hda_codec *codec) spec->adc_nids = alc662_adc_nids; spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); spec->capsrc_nids = alc662_capsrc_nids; + spec->is_mix_capture = 1; + + if (!spec->cap_mixer) + set_capture_mixer(spec); spec->vmaster_nid = 0x02; @@ -16292,6 +16604,7 @@ 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; } @@ -16299,12 +16612,13 @@ static int patch_alc662(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_realtek[] = { +static struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, + { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, @@ -16323,7 +16637,33 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A", .patch = patch_alc882 }, /* should be patch_alc883() in future */ { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, + { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", + .patch = patch_alc883 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:10ec*"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek HD-audio codec"); + +static struct hda_codec_preset_list realtek_list = { + .preset = snd_hda_preset_realtek, + .owner = THIS_MODULE, +}; + +static int __init patch_realtek_init(void) +{ + return snd_hda_add_codec_preset(&realtek_list); +} + +static void __exit patch_realtek_exit(void) +{ + snd_hda_delete_codec_preset(&realtek_list); +} + +module_init(patch_realtek_init) +module_exit(patch_realtek_exit) diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 9332b63e406c..43b436c5d01b 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -28,7 +28,6 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" /* si3054 verbs */ #define SI3054_VERB_READ_NODE 0x900 @@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_si3054[] = { +static struct hda_codec_preset snd_hda_preset_si3054[] = { { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, @@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_si3054[] = { {} }; +MODULE_ALIAS("snd-hda-codec-id:163c3055"); +MODULE_ALIAS("snd-hda-codec-id:163c3155"); +MODULE_ALIAS("snd-hda-codec-id:11c13026"); +MODULE_ALIAS("snd-hda-codec-id:11c13055"); +MODULE_ALIAS("snd-hda-codec-id:11c13155"); +MODULE_ALIAS("snd-hda-codec-id:10573055"); +MODULE_ALIAS("snd-hda-codec-id:10573057"); +MODULE_ALIAS("snd-hda-codec-id:10573155"); +MODULE_ALIAS("snd-hda-codec-id:11063288"); +MODULE_ALIAS("snd-hda-codec-id:15433155"); +MODULE_ALIAS("snd-hda-codec-id:18540018"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); + +static struct hda_codec_preset_list si3054_list = { + .preset = snd_hda_preset_si3054, + .owner = THIS_MODULE, +}; + +static int __init patch_si3054_init(void) +{ + return snd_hda_add_codec_preset(&si3054_list); +} + +static void __exit patch_si3054_exit(void) +{ + snd_hda_delete_codec_preset(&si3054_list); +} + +module_init(patch_si3054_init) +module_exit(patch_si3054_exit) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c59065513118..35b83dc6e19e 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -30,15 +30,17 @@ #include <linux/pci.h> #include <sound/core.h> #include <sound/asoundef.h> +#include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" #include "hda_beep.h" -#define NUM_CONTROL_ALLOC 32 -#define STAC_PWR_EVENT 0x20 -#define STAC_HP_EVENT 0x30 -#define STAC_VREF_EVENT 0x40 +enum { + STAC_VREF_EVENT = 1, + STAC_INSERT_EVENT, + STAC_PWR_EVENT, + STAC_HP_EVENT, +}; enum { STAC_REF, @@ -67,8 +69,12 @@ enum { }; enum { + STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, - STAC_DELL_M6, + STAC_DELL_M6_AMIC, + STAC_DELL_M6_DMIC, + STAC_DELL_M6_BOTH, + STAC_DELL_EQ, STAC_92HD73XX_MODELS }; @@ -81,6 +87,7 @@ enum { STAC_92HD71BXX_REF, STAC_DELL_M4_1, STAC_DELL_M4_2, + STAC_DELL_M4_3, STAC_HP_M4, STAC_92HD71BXX_MODELS }; @@ -121,6 +128,7 @@ enum { }; enum { + STAC_D965_REF_NO_JD, /* no jack-detection */ STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, @@ -129,14 +137,26 @@ enum { STAC_927X_MODELS }; +struct sigmatel_event { + hda_nid_t nid; + unsigned char type; + unsigned char tag; + int data; +}; + +struct sigmatel_jack { + hda_nid_t nid; + int type; + struct snd_jack *jack; +}; + struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; int board_config; + unsigned int eapd_switch: 1; unsigned int surr_switch: 1; - unsigned int line_switch: 1; - unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; unsigned int spdif_mute: 1; @@ -161,12 +181,20 @@ struct sigmatel_spec { hda_nid_t *pwr_nids; hda_nid_t *dac_list; + /* jack detection */ + struct snd_array jacks; + + /* events */ + struct snd_array events; + /* playback */ struct hda_input_mux *mono_mux; struct hda_input_mux *amp_mux; unsigned int cur_mmux; struct hda_multi_out multiout; hda_nid_t dac_nids[5]; + hda_nid_t hp_dacs[5]; + hda_nid_t speaker_dacs[5]; /* capture */ hda_nid_t *adc_nids; @@ -190,7 +218,6 @@ struct sigmatel_spec { hda_nid_t *pin_nids; unsigned int num_pins; unsigned int *pin_configs; - unsigned int *bios_pin_configs; /* codec specific stuff */ struct hda_verb *init; @@ -211,15 +238,16 @@ struct sigmatel_spec { /* i/o switches */ unsigned int io_switch[2]; unsigned int clfe_swap; - unsigned int hp_switch; + hda_nid_t line_switch; /* shared line-in for input and output */ + hda_nid_t mic_switch; /* shared mic-in for input and output */ + hda_nid_t hp_switch; /* NID of HP as line-out */ unsigned int aloopback; struct hda_pcm pcm_rec[2]; /* PCM information */ /* dynamic controls and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; + struct snd_array kctls; struct hda_input_mux private_dimux; struct hda_input_mux private_imux; struct hda_input_mux private_smux; @@ -263,9 +291,6 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { }; #define STAC92HD73_DAC_COUNT 5 -static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { - 0x15, 0x16, 0x17, 0x18, 0x19, -}; static hda_nid_t stac92hd73xx_mux_nids[4] = { 0x28, 0x29, 0x2a, 0x2b, @@ -284,11 +309,7 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { 0x11, 0x12, 0 }; -#define STAC92HD81_DAC_COUNT 2 #define STAC92HD83_DAC_COUNT 3 -static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = { - 0x13, 0x14, 0x22, -}; static hda_nid_t stac92hd83xxx_dmux_nids[2] = { 0x17, 0x18, @@ -330,10 +351,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = { 0x24, 0x25, }; -static hda_nid_t stac92hd71bxx_dac_nids[1] = { - 0x10, /*0x11, */ -}; - #define STAC92HD71BXX_NUM_DMICS 2 static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { 0x18, 0x19, 0 @@ -565,14 +582,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, else nid = codec->slave_dig_outs[smux_idx - 1]; if (spec->cur_smux[smux_idx] == smux->num_items - 1) - val = AMP_OUT_MUTE; - if (smux_idx == 0) - nid = spec->multiout.dig_out_nid; + val = HDA_AMP_MUTE; else - nid = codec->slave_dig_outs[smux_idx - 1]; + val = 0; /* un/mute SPDIF out */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, val); + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, val); } return 0; } @@ -737,10 +752,6 @@ static struct hda_verb stac9200_eapd_init[] = { static struct hda_verb stac92hd73xx_6ch_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* setup audio connections */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, - { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, /* setup adcs to point to mixer */ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -759,10 +770,6 @@ static struct hda_verb dell_eq_core_init[] = { /* set master volume to max value without distortion * and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, - /* setup audio connections */ - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02}, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01}, /* setup adcs to point to mixer */ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -775,13 +782,7 @@ static struct hda_verb dell_eq_core_init[] = { }; static struct hda_verb dell_m6_core_init[] = { - /* set master volume to max value without distortion - * and direct control */ - { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec}, - /* setup audio connections */ - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* setup adcs to point to mixer */ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -796,13 +797,6 @@ static struct hda_verb dell_m6_core_init[] = { static struct hda_verb stac92hd73xx_8ch_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* setup audio connections */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, - { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, - /* connect hp ports to dac3 */ - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, /* setup adcs to point to mixer */ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -820,15 +814,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = { static struct hda_verb stac92hd73xx_10ch_core_init[] = { /* set master volume and direct control */ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* setup audio connections */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, - { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, /* dac3 is connected to import3 mux */ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, - /* connect hp ports to dac4 */ - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, /* setup adcs to point to mixer */ { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -854,17 +841,17 @@ static struct hda_verb stac92hd83xxx_core_init[] = { /* power state controls amps */ { 0x01, AC_VERB_SET_EAPD, 1 << 2}, + {} }; static struct hda_verb stac92hd71bxx_core_init[] = { /* set master volume and direct control */ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* connect headphone jack to dac1 */ - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} }; #define HD_DISABLE_PORTF 2 @@ -879,8 +866,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = { /* set master volume and direct control */ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, - /* connect headphone jack to dac1 */ - { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* unmute right and left channels for nodes 0x0a, 0xd */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -1080,21 +1065,21 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = { HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT), - HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT), + HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT), - HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT), /* - HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT), */ { } /* end */ }; @@ -1233,9 +1218,14 @@ static const char *slave_sws[] = { NULL }; +static void stac92xx_free_kctls(struct hda_codec *codec); +static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type); + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; int err; int i; @@ -1250,7 +1240,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) } if (spec->num_dmuxes > 0) { stac_dmux_mixer.count = spec->num_dmuxes; - err = snd_ctl_add(codec->bus->card, + err = snd_hda_ctl_add(codec, snd_ctl_new1(&stac_dmux_mixer, codec)); if (err < 0) return err; @@ -1266,7 +1256,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) spec->spdif_mute = 1; } stac_smux_mixer.count = spec->num_smuxes; - err = snd_ctl_add(codec->bus->card, + err = snd_hda_ctl_add(codec, snd_ctl_new1(&stac_smux_mixer, codec)); if (err < 0) return err; @@ -1282,7 +1272,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) return err; spec->multiout.share_spdif = 1; } - if (spec->dig_in_nid && (!spec->gpio_dir & 0x01)) { + if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) { err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); if (err < 0) return err; @@ -1305,6 +1295,37 @@ static int stac92xx_build_controls(struct hda_codec *codec) return err; } + stac92xx_free_kctls(codec); /* no longer needed */ + + /* create jack input elements */ + if (spec->hp_detect) { + for (i = 0; i < cfg->hp_outs; i++) { + int type = SND_JACK_HEADPHONE; + nid = cfg->hp_pins[i]; + /* jack detection */ + if (cfg->hp_outs == i) + type |= SND_JACK_LINEOUT; + err = stac92xx_add_jack(codec, nid, type); + if (err < 0) + return err; + } + } + for (i = 0; i < cfg->line_outs; i++) { + err = stac92xx_add_jack(codec, cfg->line_out_pins[i], + SND_JACK_LINEOUT); + if (err < 0) + return err; + } + for (i = 0; i < AUTO_PIN_LAST; i++) { + nid = cfg->input_pins[i]; + if (nid) { + err = stac92xx_add_jack(codec, nid, + SND_JACK_MICROPHONE); + if (err < 0) + return err; + } + } + return 0; } @@ -1601,12 +1622,19 @@ static unsigned int dell_m6_pin_configs[13] = { static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, - [STAC_DELL_M6] = dell_m6_pin_configs, + [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_DMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, + [STAC_DELL_EQ] = dell_m6_pin_configs, }; static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_NO_JD] = "no-jd", [STAC_92HD73XX_REF] = "ref", - [STAC_DELL_M6] = "dell-m6", + [STAC_DELL_M6_AMIC] = "dell-m6-amic", + [STAC_DELL_M6_DMIC] = "dell-m6-dmic", + [STAC_DELL_M6_BOTH] = "dell-m6", + [STAC_DELL_EQ] = "dell-eq", }; static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { @@ -1614,19 +1642,25 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254, - "unknown Dell", STAC_DELL_M6), + "Dell Studio 1535", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_BOTH), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_BOTH), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_AMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_AMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271, - "unknown Dell", STAC_DELL_M6), + "unknown Dell", STAC_DELL_M6_DMIC), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0272, + "unknown Dell", STAC_DELL_M6_DMIC), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f, + "Dell Studio 1537", STAC_DELL_M6_DMIC), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0, + "Dell Studio 17", STAC_DELL_M6_DMIC), {} /* terminator */ }; @@ -1649,6 +1683,7 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), + {} /* terminator */ }; static unsigned int ref92hd71bxx_pin_configs[11] = { @@ -1669,10 +1704,17 @@ static unsigned int dell_m4_2_pin_configs[11] = { 0x40f000f0, 0x044413b0, 0x044413b0, }; +static unsigned int dell_m4_3_pin_configs[11] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x044413b0, 0x044413b0, +}; + static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, [STAC_DELL_M4_1] = dell_m4_1_pin_configs, [STAC_DELL_M4_2] = dell_m4_2_pin_configs, + [STAC_DELL_M4_3] = dell_m4_3_pin_configs, [STAC_HP_M4] = NULL, }; @@ -1680,6 +1722,7 @@ static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { [STAC_92HD71BXX_REF] = "ref", [STAC_DELL_M4_1] = "dell-m4-1", [STAC_DELL_M4_2] = "dell-m4-2", + [STAC_DELL_M4_3] = "dell-m4-3", [STAC_HP_M4] = "hp-m4", }; @@ -1687,6 +1730,12 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f2, + "HP dv5", STAC_HP_M4), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4, + "HP dv7", STAC_HP_M4), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc, + "HP dv7", STAC_HP_M4), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, "unknown HP", STAC_HP_M4), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, @@ -1711,6 +1760,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { "unknown Dell", STAC_DELL_M4_2), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264, "unknown Dell", STAC_DELL_M4_2), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa, + "unknown Dell", STAC_DELL_M4_3), {} /* terminator */ }; @@ -2000,6 +2051,7 @@ static unsigned int dell_3st_pin_configs[14] = { }; static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { + [STAC_D965_REF_NO_JD] = ref927x_pin_configs, [STAC_D965_REF] = ref927x_pin_configs, [STAC_D965_3ST] = d965_3st_pin_configs, [STAC_D965_5ST] = d965_5st_pin_configs, @@ -2008,6 +2060,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { }; static const char *stac927x_models[STAC_927X_MODELS] = { + [STAC_D965_REF_NO_JD] = "ref-no-jd", [STAC_D965_REF] = "ref", [STAC_D965_3ST] = "3stack", [STAC_D965_5ST] = "5stack", @@ -2166,12 +2219,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec) int i; struct sigmatel_spec *spec = codec->spec; - if (! spec->bios_pin_configs) { - spec->bios_pin_configs = kcalloc(spec->num_pins, - sizeof(*spec->bios_pin_configs), GFP_KERNEL); - if (! spec->bios_pin_configs) - return -ENOMEM; - } + kfree(spec->pin_configs); + spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs), + GFP_KERNEL); + if (!spec->pin_configs) + return -ENOMEM; for (i = 0; i < spec->num_pins; i++) { hda_nid_t nid = spec->pin_nids[i]; @@ -2181,7 +2233,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec) AC_VERB_GET_CONFIG_DEFAULT, 0x00); snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n", nid, pin_cfg); - spec->bios_pin_configs[i] = pin_cfg; + spec->pin_configs[i] = pin_cfg; } return 0; @@ -2223,6 +2275,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec) spec->pin_configs[i]); } +static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins) +{ + struct sigmatel_spec *spec = codec->spec; + + if (!pins) + return stac92xx_save_bios_config_regs(codec); + + kfree(spec->pin_configs); + spec->pin_configs = kmemdup(pins, + spec->num_pins * sizeof(*pins), + GFP_KERNEL); + if (!spec->pin_configs) + return -ENOMEM; + + stac92xx_set_config_regs(codec); + return 0; +} + +static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid, + unsigned int cfg) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_pins; i++) { + if (spec->pin_nids[i] == nid) { + spec->pin_configs[i] = cfg; + stac92xx_set_config_reg(codec, nid, cfg); + break; + } + } +} + /* * Analog playback callbacks */ @@ -2300,7 +2385,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo, if (spec->powerdown_adcs) { msleep(40); - snd_hda_codec_write_cache(codec, nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); } snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); @@ -2316,7 +2401,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, snd_hda_codec_cleanup_stream(codec, nid); if (spec->powerdown_adcs) - snd_hda_codec_write_cache(codec, nid, 0, + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); return 0; } @@ -2444,22 +2529,26 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->hp_switch; + ucontrol->value.integer.value[0] = !!spec->hp_switch; return 0; } +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, + unsigned char type); + static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - - spec->hp_switch = ucontrol->value.integer.value[0]; + int nid = kcontrol->private_value; + + spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; /* check to be sure that the ports are upto date with * switch changes */ - codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); return 1; } @@ -2499,7 +2588,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ * appropriately according to the pin direction */ if (spec->hp_detect) - codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + stac_issue_unsol_event(codec, nid, STAC_HP_EVENT); return 1; } @@ -2587,108 +2676,90 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = { }; /* add dynamic controls */ -static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type, - int idx, const char *name, unsigned long val) +static int stac92xx_add_control_temp(struct sigmatel_spec *spec, + struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) { struct snd_kcontrol_new *knew; - if (spec->num_kctl_used >= spec->num_kctl_alloc) { - int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; - - knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ - if (! knew) - return -ENOMEM; - if (spec->kctl_alloc) { - memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); - kfree(spec->kctl_alloc); - } - spec->kctl_alloc = knew; - spec->num_kctl_alloc = num; - } - - knew = &spec->kctl_alloc[spec->num_kctl_used]; - *knew = stac92xx_control_templates[type]; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; + *knew = *ktemp; knew->index = idx; knew->name = kstrdup(name, GFP_KERNEL); - if (! knew->name) + if (!knew->name) return -ENOMEM; knew->private_value = val; - spec->num_kctl_used++; return 0; } +static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec, + int type, int idx, const char *name, + unsigned long val) +{ + return stac92xx_add_control_temp(spec, + &stac92xx_control_templates[type], + idx, name, val); +} + /* add dynamic controls */ -static int stac92xx_add_control(struct sigmatel_spec *spec, int type, - const char *name, unsigned long val) +static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type, + const char *name, unsigned long val) { return stac92xx_add_control_idx(spec, type, 0, name, val); } -/* flag inputs as additional dynamic lineouts */ -static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg) +/* check whether the line-input can be used as line-out */ +static hda_nid_t check_line_out_switch(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - unsigned int wcaps, wtype; - int i, num_dacs = 0; - - /* use the wcaps cache to count all DACs available for line-outs */ - for (i = 0; i < codec->num_nodes; i++) { - wcaps = codec->wcaps[i]; - wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + unsigned int pincap; - if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) - num_dacs++; - } + if (cfg->line_out_type != AUTO_PIN_LINE_OUT) + return 0; + nid = cfg->input_pins[AUTO_PIN_LINE]; + pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) + return nid; + return 0; +} - snd_printdd("%s: total dac count=%d\n", __func__, num_dacs); - - switch (cfg->line_outs) { - case 3: - /* add line-in as side */ - if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) { - cfg->line_out_pins[cfg->line_outs] = - cfg->input_pins[AUTO_PIN_LINE]; - spec->line_switch = 1; - cfg->line_outs++; - } - break; - case 2: - /* add line-in as clfe and mic as side */ - if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) { - cfg->line_out_pins[cfg->line_outs] = - cfg->input_pins[AUTO_PIN_LINE]; - spec->line_switch = 1; - cfg->line_outs++; - } - if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) { - cfg->line_out_pins[cfg->line_outs] = - cfg->input_pins[AUTO_PIN_MIC]; - spec->mic_switch = 1; - cfg->line_outs++; - } - break; - case 1: - /* add line-in as surr and mic as clfe */ - if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) { - cfg->line_out_pins[cfg->line_outs] = - cfg->input_pins[AUTO_PIN_LINE]; - spec->line_switch = 1; - cfg->line_outs++; - } - if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) { - cfg->line_out_pins[cfg->line_outs] = - cfg->input_pins[AUTO_PIN_MIC]; - spec->mic_switch = 1; - cfg->line_outs++; +/* check whether the mic-input can be used as line-out */ +static hda_nid_t check_mic_out_switch(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int def_conf, pincap; + unsigned int mic_pin; + + if (cfg->line_out_type != AUTO_PIN_LINE_OUT) + return 0; + mic_pin = AUTO_PIN_MIC; + for (;;) { + hda_nid_t nid = cfg->input_pins[mic_pin]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + /* some laptops have an internal analog microphone + * which can't be used as a output */ + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { + pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) + return nid; } - break; + if (mic_pin == AUTO_PIN_MIC) + mic_pin = AUTO_PIN_FRONT_MIC; + else + break; } - return 0; } - static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) { int i; @@ -2701,6 +2772,52 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) return 0; } +static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + if (is_in_dac_nids(spec, nid)) + return 1; + for (i = 0; i < spec->autocfg.hp_outs; i++) + if (spec->hp_dacs[i] == nid) + return 1; + for (i = 0; i < spec->autocfg.speaker_outs; i++) + if (spec->speaker_dacs[i] == nid) + return 1; + return 0; +} + +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + int j, conn_len; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + unsigned int wcaps, wtype; + + conn_len = snd_hda_get_connections(codec, nid, conn, + HDA_MAX_CONNECTIONS); + for (j = 0; j < conn_len; j++) { + wcaps = snd_hda_param_read(codec, conn[j], + AC_PAR_AUDIO_WIDGET_CAP); + wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + /* we check only analog outputs */ + if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) + continue; + /* if this route has a free DAC, assign it */ + if (!check_all_dac_nids(spec, conn[j])) { + if (conn_len > 1) { + /* select this DAC in the pin's input mux */ + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, j); + } + return conn[j]; + } + } + return 0; +} + +static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid); +static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid); + /* * Fill in the dac_nids table from the parsed pin configuration * This function only works when every pin in line_out_pins[] @@ -2708,31 +2825,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) * codecs are not connected directly to a DAC, such as the 9200 * and 9202/925x. For those, dac_nids[] must be hard-coded. */ -static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, - struct auto_pin_cfg *cfg) +static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i, j, conn_len = 0; - hda_nid_t nid, conn[HDA_MAX_CONNECTIONS]; - unsigned int wcaps, wtype; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + hda_nid_t nid, dac; for (i = 0; i < cfg->line_outs; i++) { nid = cfg->line_out_pins[i]; - conn_len = snd_hda_get_connections(codec, nid, conn, - HDA_MAX_CONNECTIONS); - for (j = 0; j < conn_len; j++) { - wcaps = snd_hda_param_read(codec, conn[j], - AC_PAR_AUDIO_WIDGET_CAP); - wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wtype != AC_WID_AUD_OUT || - (wcaps & AC_WCAP_DIGITAL)) - continue; - /* conn[j] is a DAC routed to this line-out */ - if (!is_in_dac_nids(spec, conn[j])) - break; - } - - if (j == conn_len) { + dac = get_unassigned_dac(codec, nid); + if (!dac) { if (spec->multiout.num_dacs > 0) { /* we have already working output pins, * so let's drop the broken ones again @@ -2746,24 +2849,64 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, __func__, nid); return -ENODEV; } + add_spec_dacs(spec, dac); + } - spec->multiout.dac_nids[i] = conn[j]; - spec->multiout.num_dacs++; - if (conn_len > 1) { - /* select this DAC in the pin's input mux */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, j); + /* add line-in as output */ + nid = check_line_out_switch(codec); + if (nid) { + dac = get_unassigned_dac(codec, nid); + if (dac) { + snd_printdd("STAC: Add line-in 0x%x as output %d\n", + nid, cfg->line_outs); + cfg->line_out_pins[cfg->line_outs] = nid; + cfg->line_outs++; + spec->line_switch = nid; + add_spec_dacs(spec, dac); + } + } + /* add mic as output */ + nid = check_mic_out_switch(codec); + if (nid) { + dac = get_unassigned_dac(codec, nid); + if (dac) { + snd_printdd("STAC: Add mic-in 0x%x as output %d\n", + nid, cfg->line_outs); + cfg->line_out_pins[cfg->line_outs] = nid; + cfg->line_outs++; + spec->mic_switch = nid; + add_spec_dacs(spec, dac); + } + } + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + dac = get_unassigned_dac(codec, nid); + if (dac) { + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = dac; + else + add_spec_extra_dacs(spec, dac); } + spec->hp_dacs[i] = dac; + } + + for (i = 0; i < cfg->speaker_outs; i++) { + nid = cfg->speaker_pins[i]; + dac = get_unassigned_dac(codec, nid); + if (dac) + add_spec_extra_dacs(spec, dac); + spec->speaker_dacs[i] = dac; } - snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", spec->multiout.num_dacs, spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], spec->multiout.dac_nids[3], spec->multiout.dac_nids[4]); + return 0; } @@ -2788,9 +2931,7 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) { - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - else if (spec->multiout.num_dacs > 4) { + if (spec->multiout.num_dacs > 4) { printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid); return 1; } else { @@ -2800,35 +2941,47 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid) return 0; } -static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid) +static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid) { - if (is_in_dac_nids(spec, nid)) - return 1; + int i; + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { + if (!spec->multiout.extra_out_nid[i]) { + spec->multiout.extra_out_nid[i] = nid; + return 0; + } + } + printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid); + return 1; +} + +static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid) +{ + int i; + + if (spec->autocfg.line_outs != 1) + return 0; if (spec->multiout.hp_nid == nid) - return 1; - return 0; + return 0; + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) + if (spec->multiout.extra_out_nid[i] == nid) + return 0; + return 1; } /* add playback controls from the parsed DAC table */ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) { + struct sigmatel_spec *spec = codec->spec; static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; - hda_nid_t nid; + hda_nid_t nid = 0; int i, err; + unsigned int wid_caps; - struct sigmatel_spec *spec = codec->spec; - unsigned int wid_caps, pincap; - - - for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) { - if (!spec->multiout.dac_nids[i]) - continue; - + for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) { nid = spec->multiout.dac_nids[i]; - if (i == 2) { /* Center/LFE */ err = create_controls(spec, "Center", nid, 1); @@ -2850,64 +3003,47 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } else { - err = create_controls(spec, chname[i], nid, 3); + const char *name = chname[i]; + /* if it's a single DAC, assign a better name */ + if (!i && is_unique_dac(spec, nid)) { + switch (cfg->line_out_type) { + case AUTO_PIN_HP_OUT: + name = "Headphone"; + break; + case AUTO_PIN_SPEAKER_OUT: + name = "Speaker"; + break; + } + } + err = create_controls(spec, name, nid, 3); if (err < 0) return err; } } - if ((spec->multiout.num_dacs - cfg->line_outs) > 0 && - cfg->hp_outs && !spec->multiout.hp_nid) - spec->multiout.hp_nid = nid; - - if (cfg->hp_outs > 1) { + if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_HP_SWITCH, - "Headphone as Line Out Switch", 0); + "Headphone as Line Out Switch", + cfg->hp_pins[cfg->hp_outs - 1]); if (err < 0) return err; } if (spec->line_switch) { - nid = cfg->input_pins[AUTO_PIN_LINE]; - pincap = snd_hda_param_read(codec, nid, - AC_PAR_PIN_CAP); - if (pincap & AC_PINCAP_OUT) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_IO_SWITCH, - "Line In as Output Switch", nid << 8); - if (err < 0) - return err; - } + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, + "Line In as Output Switch", + spec->line_switch << 8); + if (err < 0) + return err; } if (spec->mic_switch) { - unsigned int def_conf; - unsigned int mic_pin = AUTO_PIN_MIC; -again: - nid = cfg->input_pins[mic_pin]; - def_conf = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); - /* some laptops have an internal analog microphone - * which can't be used as a output */ - if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { - pincap = snd_hda_param_read(codec, nid, - AC_PAR_PIN_CAP); - if (pincap & AC_PINCAP_OUT) { - err = stac92xx_add_control(spec, - STAC_CTL_WIDGET_IO_SWITCH, - "Mic as Output Switch", (nid << 8) | 1); - nid = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - if (!check_in_dac_nids(spec, nid)) - add_spec_dacs(spec, nid); - if (err < 0) - return err; - } - } else if (mic_pin == AUTO_PIN_MIC) { - mic_pin = AUTO_PIN_FRONT_MIC; - goto again; - } + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, + "Mic as Output Switch", + (spec->mic_switch << 8) | 1); + if (err < 0) + return err; } return 0; @@ -2919,55 +3055,39 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; hda_nid_t nid; - int i, old_num_dacs, err; + int i, err, nums; - old_num_dacs = spec->multiout.num_dacs; + nums = 0; for (i = 0; i < cfg->hp_outs; i++) { + static const char *pfxs[] = { + "Headphone", "Headphone2", "Headphone3", + }; unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]); if (wid_caps & AC_WCAP_UNSOL_CAP) spec->hp_detect = 1; - nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - if (check_in_dac_nids(spec, nid)) - nid = 0; - if (! nid) + if (nums >= ARRAY_SIZE(pfxs)) continue; - add_spec_dacs(spec, nid); - } - for (i = 0; i < cfg->speaker_outs; i++) { - nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - if (check_in_dac_nids(spec, nid)) - nid = 0; - if (! nid) + nid = spec->hp_dacs[i]; + if (!nid) continue; - add_spec_dacs(spec, nid); - } - for (i = 0; i < cfg->line_outs; i++) { - nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0, - AC_VERB_GET_CONNECT_LIST, 0) & 0xff; - if (check_in_dac_nids(spec, nid)) - nid = 0; - if (! nid) - continue; - add_spec_dacs(spec, nid); + err = create_controls(spec, pfxs[nums++], nid, 3); + if (err < 0) + return err; } - for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) { + nums = 0; + for (i = 0; i < cfg->speaker_outs; i++) { static const char *pfxs[] = { "Speaker", "External Speaker", "Speaker2", }; - err = create_controls(spec, pfxs[i - old_num_dacs], - spec->multiout.dac_nids[i], 3); - if (err < 0) - return err; - } - if (spec->multiout.hp_nid) { - err = create_controls(spec, "Headphone", - spec->multiout.hp_nid, 3); + if (nums >= ARRAY_SIZE(pfxs)) + continue; + nid = spec->speaker_dacs[i]; + if (!nid) + continue; + err = create_controls(spec, pfxs[nums++], nid, 3); if (err < 0) return err; } - return 0; } @@ -3061,6 +3181,43 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, return 0; } +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info + +static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = codec->beep->enabled; + return 0; +} + +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; +} + +static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = stac92xx_dig_beep_switch_info, + .get = stac92xx_dig_beep_switch_get, + .put = stac92xx_dig_beep_switch_put, +}; + +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); +} +#endif + static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -3268,7 +3425,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out { struct sigmatel_spec *spec = codec->spec; int err; - int hp_speaker_swap = 0; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -3286,13 +3442,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out * speaker_outs so that the following routines can handle * HP pins as primary outputs. */ + snd_printdd("stac92xx: Enabling multi-HPs workaround\n"); memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, sizeof(spec->autocfg.line_out_pins)); spec->autocfg.speaker_outs = spec->autocfg.line_outs; memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, sizeof(spec->autocfg.hp_pins)); spec->autocfg.line_outs = spec->autocfg.hp_outs; - hp_speaker_swap = 1; + spec->autocfg.line_out_type = AUTO_PIN_HP_OUT; + spec->autocfg.hp_outs = 0; } if (spec->autocfg.mono_out_pin) { int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & @@ -3344,11 +3502,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out AC_PINCTL_OUT_EN); } - if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) - return err; - if (spec->multiout.num_dacs == 0) - if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + if (!spec->multiout.num_dacs) { + err = stac92xx_auto_fill_dac_nids(codec); + if (err < 0) return err; + } err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg); @@ -3367,6 +3525,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out #ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->digbeep_nid > 0) { hda_nid_t nid = spec->digbeep_nid; + unsigned int caps; err = stac92xx_auto_create_beep_ctls(codec, nid); if (err < 0) @@ -3374,22 +3533,17 @@ 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; + /* 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; + } } #endif - if (hp_speaker_swap == 1) { - /* Restore the hp_outs and line_outs */ - memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, - sizeof(spec->autocfg.line_out_pins)); - spec->autocfg.hp_outs = spec->autocfg.line_outs; - memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, - sizeof(spec->autocfg.speaker_pins)); - spec->autocfg.line_outs = spec->autocfg.speaker_outs; - memset(spec->autocfg.speaker_pins, 0, - sizeof(spec->autocfg.speaker_pins)); - spec->autocfg.speaker_outs = 0; - } - err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); if (err < 0) @@ -3434,11 +3588,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (dig_in && spec->autocfg.dig_in_pin) spec->dig_in_nid = dig_in; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; + if (!spec->dinput_mux) + spec->dinput_mux = &spec->private_dimux; spec->sinput_mux = &spec->private_smux; spec->mono_mux = &spec->private_mono_mux; spec->amp_mux = &spec->private_amp_mux; @@ -3531,13 +3686,19 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0) return err; + if (spec->num_muxes > 0) { + err = stac92xx_auto_create_mux_input_ctls(codec); + if (err < 0) + return err; + } + if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = 0x05; if (spec->autocfg.dig_in_pin) spec->dig_in_nid = 0x04; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux; spec->dinput_mux = &spec->private_dimux; @@ -3581,13 +3742,101 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } +static int stac92xx_add_jack(struct hda_codec *codec, + hda_nid_t nid, int type) +{ +#ifdef CONFIG_SND_JACK + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_jack *jack; + int def_conf = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_CONFIG_DEFAULT, 0); + int connectivity = get_defcfg_connect(def_conf); + char name[32]; + + if (connectivity && connectivity != AC_JACK_PORT_FIXED) + return 0; + + snd_array_init(&spec->jacks, sizeof(*jack), 32); + jack = snd_array_new(&spec->jacks); + if (!jack) + return -ENOMEM; + jack->nid = nid; + jack->type = type; + + sprintf(name, "%s at %s %s Jack", + snd_hda_get_jack_type(def_conf), + snd_hda_get_jack_connectivity(def_conf), + snd_hda_get_jack_location(def_conf)); + + return snd_jack_new(codec->bus->card, name, type, &jack->jack); +#else + return 0; +#endif +} + +static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid, + unsigned char type, int data) +{ + struct sigmatel_event *event; + + snd_array_init(&spec->events, sizeof(*event), 32); + event = snd_array_new(&spec->events); + if (!event) + return -ENOMEM; + event->nid = nid; + event->type = type; + event->tag = spec->events.used; + event->data = data; + + return event->tag; +} + +static struct sigmatel_event *stac_get_event(struct hda_codec *codec, + hda_nid_t nid, unsigned char type) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_event *event = spec->events.list; + int i; + + for (i = 0; i < spec->events.used; i++, event++) { + if (event->nid == nid && event->type == type) + return event; + } + return NULL; +} + +static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec, + unsigned char tag) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_event *event = spec->events.list; + int i; + + for (i = 0; i < spec->events.used; i++, event++) { + if (event->tag == tag) + return event; + } + return NULL; +} + static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, - unsigned int event) + unsigned int type) { - if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - (AC_USRSP_EN | event)); + struct sigmatel_event *event; + int tag; + + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) + return; + event = stac_get_event(codec, nid, type); + if (event) + tag = event->tag; + else + tag = stac_add_event(codec->spec, nid, type, 0); + if (tag < 0) + return; + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | tag); } static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) @@ -3607,16 +3856,19 @@ static void stac92xx_power_down(struct hda_codec *codec) /* power down inactive DACs */ hda_nid_t *dac; for (dac = spec->dac_list; *dac; dac++) - if (!is_in_dac_nids(spec, *dac) && - spec->multiout.hp_nid != *dac) - snd_hda_codec_write_cache(codec, *dac, 0, + if (!check_all_dac_nids(spec, *dac)) + snd_hda_codec_write(codec, *dac, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); } +static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + int enable); + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int gpio; int i; snd_hda_sequence_write(codec, spec->init); @@ -3624,96 +3876,159 @@ static int stac92xx_init(struct hda_codec *codec) /* power down adcs initially */ if (spec->powerdown_adcs) for (i = 0; i < spec->num_adcs; i++) - snd_hda_codec_write_cache(codec, + snd_hda_codec_write(codec, spec->adc_nids[i], 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + /* set up GPIO */ + gpio = spec->gpio_data; + /* turn on EAPD statically when spec->eapd_switch isn't set. + * otherwise, unsol event will turn it on/off dynamically + */ + if (!spec->eapd_switch) + gpio |= spec->eapd_mask; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio); + /* set up pins */ if (spec->hp_detect) { /* Enable unsolicited responses on the HP widget */ - for (i = 0; i < cfg->hp_outs; i++) - enable_pin_detect(codec, cfg->hp_pins[i], - STAC_HP_EVENT); + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + enable_pin_detect(codec, nid, STAC_HP_EVENT); + } /* force to enable the first line-out; the others are set up * in unsol_event */ stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0], - AC_PINCTL_OUT_EN); - stac92xx_auto_init_hp_out(codec); + AC_PINCTL_OUT_EN); /* fake event to set up pins */ - codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], + STAC_HP_EVENT); } else { stac92xx_auto_init_multi_out(codec); stac92xx_auto_init_hp_out(codec); + for (i = 0; i < cfg->hp_outs; i++) + stac_toggle_power_map(codec, cfg->hp_pins[i], 1); } for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = cfg->input_pins[i]; if (nid) { - unsigned int pinctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* if PINCTL already set then skip */ - if (pinctl & AC_PINCAP_IN) - continue; - pinctl = AC_PINCTL_IN_EN; - if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) - pinctl |= stac92xx_get_vref(codec, nid); - stac92xx_auto_set_pinctl(codec, nid, pinctl); + unsigned int pinctl, conf; + if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { + /* for mic pins, force to initialize */ + pinctl = stac92xx_get_vref(codec, nid); + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, pinctl); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ + if (!(pinctl & AC_PINCTL_IN_EN)) { + pinctl |= AC_PINCTL_IN_EN; + stac92xx_auto_set_pinctl(codec, nid, + pinctl); + } + } + conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) { + enable_pin_detect(codec, nid, + STAC_INSERT_EVENT); + stac_issue_unsol_event(codec, nid, + STAC_INSERT_EVENT); + } } } for (i = 0; i < spec->num_dmics; i++) stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], AC_PINCTL_IN_EN); + if (cfg->dig_out_pin) + stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, + AC_PINCTL_OUT_EN); + if (cfg->dig_in_pin) + stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, + AC_PINCTL_IN_EN); for (i = 0; i < spec->num_pwrs; i++) { - int event = is_nid_hp_pin(cfg, spec->pwr_nids[i]) - ? STAC_HP_EVENT : STAC_PWR_EVENT; - int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i], - 0, AC_VERB_GET_CONFIG_DEFAULT, 0); - def_conf = get_defcfg_connect(def_conf); + hda_nid_t nid = spec->pwr_nids[i]; + int pinctl, def_conf; + + /* power on when no jack detection is available */ + if (!spec->hp_detect) { + stac_toggle_power_map(codec, nid, 1); + continue; + } + + if (is_nid_hp_pin(cfg, nid)) + continue; /* already has an unsol event */ + + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); /* outputs are only ports capable of power management * any attempts on powering down a input port cause the * referenced VREF to act quirky. */ - if (pinctl & AC_PINCTL_IN_EN) + if (pinctl & AC_PINCTL_IN_EN) { + stac_toggle_power_map(codec, nid, 1); continue; + } + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + def_conf = get_defcfg_connect(def_conf); /* skip any ports that don't have jacks since presence * detection is useless */ - if (def_conf && def_conf != AC_JACK_PORT_FIXED) + if (def_conf != AC_JACK_PORT_COMPLEX) { + if (def_conf != AC_JACK_PORT_NONE) + stac_toggle_power_map(codec, nid, 1); continue; - enable_pin_detect(codec, spec->pwr_nids[i], event | i); - codec->patch_ops.unsol_event(codec, (event | i) << 26); + } + if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) { + enable_pin_detect(codec, nid, STAC_PWR_EVENT); + stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT); + } } if (spec->dac_list) stac92xx_power_down(codec); - if (cfg->dig_out_pin) - stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, - AC_PINCTL_OUT_EN); - if (cfg->dig_in_pin) - stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, - AC_PINCTL_IN_EN); + return 0; +} + +static void stac92xx_free_jacks(struct hda_codec *codec) +{ +#ifdef CONFIG_SND_JACK + /* free jack instances manually when clearing/reconfiguring */ + struct sigmatel_spec *spec = codec->spec; + if (!codec->bus->shutdown && spec->jacks.list) { + struct sigmatel_jack *jacks = spec->jacks.list; + int i; + for (i = 0; i < spec->jacks.used; i++) + snd_device_free(codec->bus->card, &jacks[i].jack); + } + snd_array_free(&spec->jacks); +#endif +} - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); +static void stac92xx_free_kctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; - return 0; + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); } static void stac92xx_free(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i; if (! spec) return; - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); - } - - if (spec->bios_pin_configs) - kfree(spec->bios_pin_configs); + kfree(spec->pin_configs); + stac92xx_free_jacks(codec); + snd_array_free(&spec->events); kfree(spec); snd_hda_detach_beep_device(codec); @@ -3732,11 +4047,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, * "xxx as Output" mixer switch */ struct sigmatel_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - if ((nid == cfg->input_pins[AUTO_PIN_LINE] && - spec->line_switch) || - (nid == cfg->input_pins[AUTO_PIN_MIC] && - spec->mic_switch)) + if (nid == spec->line_switch || nid == spec->mic_switch) return; } @@ -3760,28 +4071,38 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static 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)) { - unsigned int pinctl; - pinctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - if (pinctl & AC_PINCTL_IN_EN) - return 0; /* mic- or line-input */ - else - return 1; /* HP-output */ - } + & (1 << 31)) + return 1; return 0; } -static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) +/* return non-zero if the hp-pin of the given array index isn't + * a jack-detection target + */ +static int no_hp_sensing(struct sigmatel_spec *spec, int i) +{ + struct auto_pin_cfg *cfg = &spec->autocfg; + + /* ignore sensing of shared line and mic jacks */ + if (cfg->hp_pins[i] == spec->line_switch) + return 1; + if (cfg->hp_pins[i] == spec->mic_switch) + return 1; + /* ignore if the pin is set as line-out */ + if (cfg->hp_pins[i] == spec->hp_switch) + return 1; + return 0; +} + +static void stac92xx_hp_detect(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int nid = cfg->hp_pins[cfg->hp_outs - 1]; int i, presence; presence = 0; @@ -3792,52 +4113,73 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) for (i = 0; i < cfg->hp_outs; i++) { if (presence) break; - if (spec->hp_switch && cfg->hp_pins[i] == nid) - break; - presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); + if (no_hp_sensing(spec, i)) + continue; + presence = get_pin_presence(codec, cfg->hp_pins[i]); + if (presence) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + presence = 0; /* mic- or line-input */ + } } if (presence) { - /* disable lineouts, enable hp */ + /* disable lineouts */ if (spec->hp_switch) - stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN); + stac92xx_reset_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); for (i = 0; i < cfg->speaker_outs; i++) stac92xx_reset_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); - if (spec->eapd_mask) + if (spec->eapd_mask && spec->eapd_switch) stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data & ~spec->eapd_mask); } else { - /* enable lineouts, disable hp */ + /* enable lineouts */ if (spec->hp_switch) - stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + stac92xx_set_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_set_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); for (i = 0; i < cfg->speaker_outs; i++) stac92xx_set_pinctl(codec, cfg->speaker_pins[i], AC_PINCTL_OUT_EN); - if (spec->eapd_mask) + if (spec->eapd_mask && spec->eapd_switch) stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data | spec->eapd_mask); } - if (!spec->hp_switch && cfg->hp_outs > 1 && presence) - stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + /* toggle hp outs */ + for (i = 0; i < cfg->hp_outs; i++) { + unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN; + if (no_hp_sensing(spec, i)) + continue; + if (presence) + stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); + else + stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); + } } -static void stac92xx_pin_sense(struct hda_codec *codec, int idx) +static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, + int enable) { struct sigmatel_spec *spec = codec->spec; - hda_nid_t nid = spec->pwr_nids[idx]; - int presence, val; - val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) - & 0x000000ff; - presence = get_hp_pin_presence(codec, nid); + unsigned int idx, val; + + for (idx = 0; idx < spec->num_pwrs; idx++) { + if (spec->pwr_nids[idx] == nid) + break; + } + if (idx >= spec->num_pwrs) + return; /* several codecs have two power down bits */ if (spec->pwr_mapping) @@ -3845,56 +4187,157 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx) else idx = 1 << idx; - if (presence) + val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff; + if (enable) val &= ~idx; else val |= idx; /* power down unused output ports */ snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val); -}; +} + +static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid) +{ + stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid)); +} + +static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + struct sigmatel_jack *jacks = spec->jacks.list; + + if (jacks) { + int i; + for (i = 0; i < spec->jacks.used; i++) { + if (jacks->nid == nid) { + unsigned int pin_ctl = + snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); + int type = jacks->type; + if (type == (SND_JACK_LINEOUT + | SND_JACK_HEADPHONE)) + type = (pin_ctl & AC_PINCTL_HP_EN) + ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT; + snd_jack_report(jacks->jack, + get_pin_presence(codec, nid) + ? type : 0); + } + jacks++; + } + } +} + +static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid, + unsigned char type) +{ + struct sigmatel_event *event = stac_get_event(codec, nid, type); + if (!event) + return; + codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26); +} static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) { struct sigmatel_spec *spec = codec->spec; - int idx = res >> 26 & 0x0f; + struct sigmatel_event *event; + int tag, data; + + tag = (res >> 26) & 0x7f; + event = stac_get_event_from_tag(codec, tag); + if (!event) + return; - switch ((res >> 26) & 0x70) { + switch (event->type) { case STAC_HP_EVENT: - stac92xx_hp_detect(codec, res); + stac92xx_hp_detect(codec); /* fallthru */ + case STAC_INSERT_EVENT: case STAC_PWR_EVENT: if (spec->num_pwrs > 0) - stac92xx_pin_sense(codec, idx); + stac92xx_pin_sense(codec, event->nid); + stac92xx_report_jack(codec, event->nid); break; - case STAC_VREF_EVENT: { - int data = snd_hda_codec_read(codec, codec->afg, 0, - AC_VERB_GET_GPIO_DATA, 0); + case STAC_VREF_EVENT: + data = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); /* toggle VREF state based on GPIOx status */ snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, - !!(data & (1 << idx))); + !!(data & (1 << event->data))); break; - } } } +#ifdef CONFIG_PROC_FS +static void stac92hd_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + if (nid == codec->afg) + snd_iprintf(buffer, "Power-Map: 0x%02x\n", + snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0)); +} + +static void analog_loop_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, + unsigned int verb) +{ + snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", + snd_hda_codec_read(codec, codec->afg, 0, verb, 0)); +} + +/* stac92hd71bxx, stac92hd73xx */ +static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + stac92hd_proc_hook(buffer, codec, nid); + if (nid == codec->afg) + analog_loop_proc_hook(buffer, codec, 0xfa0); +} + +static void stac9205_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + if (nid == codec->afg) + analog_loop_proc_hook(buffer, codec, 0xfe0); +} + +static void stac927x_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + if (nid == codec->afg) + analog_loop_proc_hook(buffer, codec, 0xfeb); +} +#else +#define stac92hd_proc_hook NULL +#define stac92hd7x_proc_hook NULL +#define stac9205_proc_hook NULL +#define stac927x_proc_hook NULL +#endif + #ifdef SND_HDA_NEEDS_RESUME static int stac92xx_resume(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; stac92xx_set_config_regs(codec); - snd_hda_sequence_write(codec, spec->init); - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); + stac92xx_init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); - /* power down inactive DACs */ - if (spec->dac_list) - stac92xx_power_down(codec); - /* invoke unsolicited event to reset the HP state */ + /* fake event to set up pins again to override cached values */ if (spec->hp_detect) - codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0], + STAC_HP_EVENT); + return 0; +} + +static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct sigmatel_spec *spec = codec->spec; + if (spec->eapd_mask) + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data & + ~spec->eapd_mask); return 0; } #endif @@ -3906,6 +4349,7 @@ static struct hda_codec_ops stac92xx_patch_ops = { .free = stac92xx_free, .unsol_event = stac92xx_unsol_event, #ifdef SND_HDA_NEEDS_RESUME + .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif }; @@ -3928,14 +4372,12 @@ static int patch_stac9200(struct hda_codec *codec) if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac9200_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac9200_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } spec->multiout.max_channels = 2; @@ -3991,14 +4433,12 @@ static int patch_stac925x(struct hda_codec *codec) snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," "using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else if (stac925x_brd_tbl[spec->board_config] != NULL){ - spec->pin_configs = stac925x_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac925x_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } spec->multiout.max_channels = 2; @@ -4062,6 +4502,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) struct sigmatel_spec *spec; hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; int err = 0; + int num_dacs; spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -4080,26 +4521,23 @@ again: snd_printdd(KERN_INFO "hda_codec: Unknown model for" " STAC92HD73XX, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac92hd73xx_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } - spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, + num_dacs = snd_hda_get_connections(codec, 0x0a, conn, STAC92HD73_DAC_COUNT + 2) - 1; - if (spec->multiout.num_dacs < 0) { + if (num_dacs < 3 || num_dacs > 5) { printk(KERN_WARNING "hda_codec: Could not determine " "number of channels defaulting to DAC count\n"); - spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; + num_dacs = STAC92HD73_DAC_COUNT; } - - switch (spec->multiout.num_dacs) { + switch (num_dacs) { case 0x3: /* 6 Channel */ spec->mixer = stac92hd73xx_6ch_mixer; spec->init = stac92hd73xx_6ch_core_init; @@ -4111,9 +4549,9 @@ again: case 0x5: /* 10 Channel */ spec->mixer = stac92hd73xx_10ch_mixer; spec->init = stac92hd73xx_10ch_core_init; - }; + } + spec->multiout.dac_nids = spec->dac_nids; - spec->multiout.dac_nids = stac92hd73xx_dac_nids; spec->aloopback_mask = 0x01; spec->aloopback_shift = 8; @@ -4133,31 +4571,32 @@ again: sizeof(stac92hd73xx_dmux)); switch (spec->board_config) { - case STAC_DELL_M6: + case STAC_DELL_EQ: spec->init = dell_eq_core_init; + /* fallthru */ + case STAC_DELL_M6_AMIC: + case STAC_DELL_M6_DMIC: + case STAC_DELL_M6_BOTH: spec->num_smuxes = 0; spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER]; spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP]; + spec->eapd_switch = 0; spec->num_amps = 1; - switch (codec->subsystem_id) { - case 0x1028025e: /* Analog Mics */ - case 0x1028025f: + + if (spec->board_config != STAC_DELL_EQ) + spec->init = dell_m6_core_init; + switch (spec->board_config) { + case STAC_DELL_M6_AMIC: /* Analog Mics */ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); spec->num_dmics = 0; spec->private_dimux.num_items = 1; break; - case 0x10280271: /* Digital Mics */ - case 0x10280272: - spec->init = dell_m6_core_init; - /* fall-through */ - case 0x10280254: - case 0x10280255: + case STAC_DELL_M6_DMIC: /* Digital Mics */ stac92xx_set_config_reg(codec, 0x13, 0x90A60160); spec->num_dmics = 1; spec->private_dimux.num_items = 2; break; - case 0x10280256: /* Both */ - case 0x10280057: + case STAC_DELL_M6_BOTH: /* Both */ stac92xx_set_config_reg(codec, 0x0b, 0x90A70170); stac92xx_set_config_reg(codec, 0x13, 0x90A60160); spec->num_dmics = 1; @@ -4168,6 +4607,7 @@ again: default: spec->num_dmics = STAC92HD73XX_NUM_DMICS; spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 1; } if (spec->board_config > STAC_92HD73XX_REF) { /* GPIO0 High = Enable EAPD */ @@ -4196,8 +4636,13 @@ again: return err; } + if (spec->board_config == STAC_92HD73XX_NO_JD) + spec->hp_detect = 0; + codec->patch_ops = stac92xx_patch_ops; + codec->proc_widget_hook = stac92hd7x_proc_hook; + return 0; } @@ -4229,17 +4674,15 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec->pwr_nids = stac92hd83xxx_pwr_nids; spec->pwr_mapping = stac92hd83xxx_pwr_mapping; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); - spec->multiout.dac_nids = stac92hd83xxx_dac_nids; + spec->multiout.dac_nids = spec->dac_nids; spec->init = stac92hd83xxx_core_init; switch (codec->vendor_id) { case 0x111d7605: - spec->multiout.num_dacs = STAC92HD81_DAC_COUNT; break; default: spec->num_pwrs--; spec->init++; /* switch to config #2 */ - spec->multiout.num_dacs = STAC92HD83_DAC_COUNT; } spec->mixer = stac92hd83xxx_mixer; @@ -4258,14 +4701,12 @@ again: snd_printdd(KERN_INFO "hda_codec: Unknown model for" " STAC92HD83XXX, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac92hd83xxx_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } err = stac92xx_parse_auto_config(codec, 0x1d, 0); @@ -4286,50 +4727,10 @@ again: codec->patch_ops = stac92xx_patch_ops; - return 0; -} - -#ifdef SND_HDA_NEEDS_RESUME -static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_POWER_STATE, pwr); - - msleep(1); - for (i = 0; i < spec->num_adcs; i++) { - snd_hda_codec_write_cache(codec, - spec->adc_nids[i], 0, - AC_VERB_SET_POWER_STATE, pwr); - } -}; - -static int stac92hd71xx_resume(struct hda_codec *codec) -{ - stac92hd71xx_set_power_state(codec, AC_PWRST_D0); - return stac92xx_resume(codec); -} + codec->proc_widget_hook = stac92hd_proc_hook; -static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state) -{ - stac92hd71xx_set_power_state(codec, AC_PWRST_D3); return 0; -}; - -#endif - -static struct hda_codec_ops stac92hd71bxx_patch_ops = { - .build_controls = stac92xx_build_controls, - .build_pcms = stac92xx_build_pcms, - .init = stac92xx_init, - .free = stac92xx_free, - .unsol_event = stac92xx_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME - .resume = stac92hd71xx_resume, - .suspend = stac92hd71xx_suspend, -#endif -}; +} static struct hda_input_mux stac92hd71bxx_dmux = { .num_items = 4, @@ -4366,14 +4767,19 @@ again: snd_printdd(KERN_INFO "hda_codec: Unknown model for" " STAC92HD71BXX, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac92hd71bxx_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; + } + + if (spec->board_config > STAC_92HD71BXX_REF) { + /* GPIO0 = EAPD */ + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; } switch (codec->vendor_id) { @@ -4386,24 +4792,24 @@ again: codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; break; case 0x111d7608: /* 5 Port with Analog Mixer */ - switch (codec->subsystem_id) { - case 0x103c361a: + switch (spec->board_config) { + case STAC_HP_M4: /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write(codec, codec->afg, 0, + err = stac_add_event(spec, codec->afg, + STAC_VREF_EVENT, 0x02); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, - (AC_USRSP_EN | STAC_VREF_EVENT | 0x01)); + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | err); spec->gpio_mask |= 0x02; break; } if ((codec->revision_id & 0xf) == 0 || - (codec->revision_id & 0xf) == 1) { -#ifdef SND_HDA_NEEDS_RESUME - codec->patch_ops = stac92hd71bxx_patch_ops; -#endif + (codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ - } /* no output amps */ spec->num_pwrs = 0; @@ -4412,15 +4818,11 @@ again: /* disable VSW */ spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF]; - stac92xx_set_config_reg(codec, 0xf, 0x40f000f0); + stac_change_pin_config(codec, 0xf, 0x40f000f0); break; case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 1) { -#ifdef SND_HDA_NEEDS_RESUME - codec->patch_ops = stac92hd71bxx_patch_ops; -#endif + if ((codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ - } /* no output amps */ spec->num_pwrs = 0; @@ -4435,13 +4837,6 @@ again: spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; - if (spec->board_config > STAC_92HD71BXX_REF) { - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; - } - spec->powerdown_adcs = 1; spec->digbeep_nid = 0x26; spec->mux_nids = stac92hd71bxx_mux_nids; @@ -4456,14 +4851,21 @@ again: switch (spec->board_config) { case STAC_HP_M4: - spec->num_dmics = 0; - spec->num_smuxes = 0; - spec->num_dmuxes = 0; - /* enable internal microphone */ - stac92xx_set_config_reg(codec, 0x0e, 0x01813040); + stac_change_pin_config(codec, 0x0e, 0x01813040); stac92xx_auto_set_pinctl(codec, 0x0e, AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); + /* fallthru */ + case STAC_DELL_M4_2: + spec->num_dmics = 0; + spec->num_smuxes = 0; + spec->num_dmuxes = 0; + break; + case STAC_DELL_M4_1: + case STAC_DELL_M4_3: + spec->num_dmics = 1; + spec->num_smuxes = 0; + spec->num_dmuxes = 0; break; default: spec->num_dmics = STAC92HD71BXX_NUM_DMICS; @@ -4471,9 +4873,7 @@ again: spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); }; - spec->multiout.num_dacs = 1; - spec->multiout.hp_nid = 0x11; - spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + spec->multiout.dac_nids = spec->dac_nids; if (spec->dinput_mux) spec->private_dimux.num_items += spec->num_dmics - @@ -4495,6 +4895,8 @@ again: return err; } + codec->proc_widget_hook = stac92hd7x_proc_hook; + return 0; }; @@ -4556,14 +4958,12 @@ static int patch_stac922x(struct hda_codec *codec) snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " "using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else if (stac922x_brd_tbl[spec->board_config] != NULL) { - spec->pin_configs = stac922x_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac922x_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } spec->adc_nids = stac922x_adc_nids; @@ -4626,14 +5026,12 @@ static int patch_stac927x(struct hda_codec *codec) snd_printdd(KERN_INFO "hda_codec: Unknown model for" "STAC927x, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac927x_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac927x_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } spec->digbeep_nid = 0x23; @@ -4663,15 +5061,15 @@ static int patch_stac927x(struct hda_codec *codec) case 0x10280209: case 0x1028022e: /* correct the device field to SPDIF out */ - stac92xx_set_config_reg(codec, 0x21, 0x01442070); + stac_change_pin_config(codec, 0x21, 0x01442070); break; }; /* configure the analog microphone on some laptops */ - stac92xx_set_config_reg(codec, 0x0c, 0x90a79130); + stac_change_pin_config(codec, 0x0c, 0x90a79130); /* correct the front output jack as a hp out */ - stac92xx_set_config_reg(codec, 0x0f, 0x0227011f); + stac_change_pin_config(codec, 0x0f, 0x0227011f); /* correct the front input jack as a mic */ - stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); + stac_change_pin_config(codec, 0x0e, 0x02a79130); /* fallthru */ case STAC_DELL_3ST: /* GPIO2 High = Enable EAPD */ @@ -4700,6 +5098,7 @@ static int patch_stac927x(struct hda_codec *codec) spec->num_pwrs = 0; spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; + spec->eapd_switch = 1; err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); if (!err) { @@ -4718,6 +5117,8 @@ static int patch_stac927x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + codec->proc_widget_hook = stac927x_proc_hook; + /* * !!FIXME!! * The STAC927x seem to require fairly long delays for certain @@ -4730,6 +5131,10 @@ static int patch_stac927x(struct hda_codec *codec) */ codec->bus->needs_damn_long_delay = 1; + /* no jack detecion for ref-no-jd model */ + if (spec->board_config == STAC_D965_REF_NO_JD) + spec->hp_detect = 0; + return 0; } @@ -4752,14 +5157,12 @@ static int patch_stac9205(struct hda_codec *codec) if (spec->board_config < 0) { snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); - if (err < 0) { - stac92xx_free(codec); - return err; - } - spec->pin_configs = spec->bios_pin_configs; - } else { - spec->pin_configs = stac9205_brd_tbl[spec->board_config]; - stac92xx_set_config_regs(codec); + } else + err = stac_save_pin_cfgs(codec, + stac9205_brd_tbl[spec->board_config]); + if (err < 0) { + stac92xx_free(codec); + return err; } spec->digbeep_nid = 0x23; @@ -4780,20 +5183,24 @@ static int patch_stac9205(struct hda_codec *codec) spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; + spec->eapd_switch = 1; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config){ case STAC_9205_DELL_M43: /* Enable SPDIF in/out */ - stac92xx_set_config_reg(codec, 0x1f, 0x01441030); - stac92xx_set_config_reg(codec, 0x20, 0x1c410030); + stac_change_pin_config(codec, 0x1f, 0x01441030); + stac_change_pin_config(codec, 0x20, 0x1c410030); /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write(codec, codec->afg, 0, + err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); snd_hda_codec_write_cache(codec, codec->afg, 0, AC_VERB_SET_UNSOLICITED_ENABLE, - (AC_USRSP_EN | STAC_HP_EVENT)); + AC_USRSP_EN | err); spec->gpio_dir = 0x0b; spec->eapd_mask = 0x01; @@ -4831,6 +5238,8 @@ static int patch_stac9205(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + codec->proc_widget_hook = stac9205_proc_hook; + return 0; } @@ -4887,29 +5296,11 @@ static struct hda_verb vaio_ar_init[] = { {} }; -/* bind volumes of both NID 0x02 and 0x05 */ -static struct hda_bind_ctls vaio_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* bind volumes of both NID 0x02 and 0x05 */ -static struct hda_bind_ctls vaio_bind_master_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - 0, - }, -}; - static struct snd_kcontrol_new vaio_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT), /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), @@ -4925,8 +5316,10 @@ static struct snd_kcontrol_new vaio_mixer[] = { }; static struct snd_kcontrol_new vaio_ar_mixer[] = { - HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), - HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT), /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), @@ -4967,7 +5360,7 @@ static int stac9872_vaio_init(struct hda_codec *codec) static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) { - if (get_hp_pin_presence(codec, 0x0a)) { + if (get_pin_presence(codec, 0x0a)) { stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); } else { @@ -5078,7 +5471,7 @@ static int patch_stac9872(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_sigmatel[] = { +static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 }, { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x }, { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x }, @@ -5142,3 +5535,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:8384*"); +MODULE_ALIAS("snd-hda-codec-id:111d*"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); + +static struct hda_codec_preset_list sigmatel_list = { + .preset = snd_hda_preset_sigmatel, + .owner = THIS_MODULE, +}; + +static int __init patch_sigmatel_init(void) +{ + return snd_hda_add_codec_preset(&sigmatel_list); +} + +static void __exit patch_sigmatel_exit(void) +{ + snd_hda_delete_codec_preset(&sigmatel_list); +} + +module_init(patch_sigmatel_init) +module_exit(patch_sigmatel_exit) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 63e4871e5d8f..c761394cbe84 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -47,15 +47,11 @@ #include <sound/asoundef.h> #include "hda_codec.h" #include "hda_local.h" -#include "hda_patch.h" /* amp values */ #define AMP_VAL_IDX_SHIFT 19 #define AMP_VAL_IDX_MASK (0x0f<<19) -#define NUM_CONTROL_ALLOC 32 -#define NUM_VERB_ALLOC 32 - /* Pin Widget NID */ #define VT1708_HP_NID 0x13 #define VT1708_DIGOUT_NID 0x14 @@ -145,8 +141,6 @@ enum { AUTO_SEQ_SIDE }; -#define get_amp_nid(kc) ((kc)->private_value & 0xffff) - /* 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, @@ -227,8 +221,7 @@ struct via_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; - unsigned int num_kctl_alloc, num_kctl_used; - struct snd_kcontrol_new *kctl_alloc; + struct snd_array kctls; struct hda_input_mux private_imux[2]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew; - if (spec->num_kctl_used >= spec->num_kctl_alloc) { - int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; - - /* array + terminator */ - knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); - if (!knew) - return -ENOMEM; - if (spec->kctl_alloc) { - memcpy(knew, spec->kctl_alloc, - sizeof(*knew) * spec->num_kctl_alloc); - kfree(spec->kctl_alloc); - } - spec->kctl_alloc = knew; - spec->num_kctl_alloc = num; - } - - knew = &spec->kctl_alloc[spec->num_kctl_used]; + snd_array_init(&spec->kctls, sizeof(*knew), 32); + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; *knew = vt1708_control_templates[type]; knew->name = kstrdup(name, GFP_KERNEL); - if (!knew->name) return -ENOMEM; knew->private_value = val; - spec->num_kctl_used++; return 0; } +static void via_free_kctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + /* 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) @@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; } + via_free_kctls(codec); /* no longer needed */ return 0; } @@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec) static void via_free(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - unsigned int i; if (!spec) return; - if (spec->kctl_alloc) { - for (i = 0; i < spec->num_kctl_used; i++) - kfree(spec->kctl_alloc[i].name); - kfree(spec->kctl_alloc); - } - + via_free_kctls(codec); kfree(codec->spec); } @@ -1373,8 +1359,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = VT1708_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; @@ -1846,8 +1832,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = VT1709_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux[0]; @@ -2390,8 +2376,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) spec->dig_in_nid = VT1708B_DIGIN_NID; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux[0]; @@ -2855,8 +2841,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec) spec->extra_dig_out_nid = 0x15; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux[0]; @@ -3174,8 +3160,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec) spec->extra_dig_out_nid = 0x1B; - if (spec->kctl_alloc) - spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; spec->input_mux = &spec->private_imux[0]; @@ -3262,74 +3248,97 @@ static int patch_vt1702(struct hda_codec *codec) /* * patch entries */ -struct hda_codec_preset snd_hda_preset_via[] = { - { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", +static struct hda_codec_preset snd_hda_preset_via[] = { + { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, + { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, + { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, + { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, + { .id = 0x1106e710, .name = "VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", + { .id = 0x1106e711, .name = "VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", + { .id = 0x1106e712, .name = "VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", + { .id = 0x1106e713, .name = "VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", + { .id = 0x1106e714, .name = "VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", + { .id = 0x1106e715, .name = "VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", + { .id = 0x1106e716, .name = "VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", + { .id = 0x1106e717, .name = "VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", + { .id = 0x1106e720, .name = "VT1708B 8-Ch", .patch = patch_vt1708B_8ch}, - { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", + { .id = 0x1106e721, .name = "VT1708B 8-Ch", .patch = patch_vt1708B_8ch}, - { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", + { .id = 0x1106e722, .name = "VT1708B 8-Ch", .patch = patch_vt1708B_8ch}, - { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", + { .id = 0x1106e723, .name = "VT1708B 8-Ch", .patch = patch_vt1708B_8ch}, - { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", + { .id = 0x1106e724, .name = "VT1708B 4-Ch", .patch = patch_vt1708B_4ch}, - { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", + { .id = 0x1106e725, .name = "VT1708B 4-Ch", .patch = patch_vt1708B_4ch}, - { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", + { .id = 0x1106e726, .name = "VT1708B 4-Ch", .patch = patch_vt1708B_4ch}, - { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", + { .id = 0x1106e727, .name = "VT1708B 4-Ch", .patch = patch_vt1708B_4ch}, - { .id = 0x11060397, .name = "VIA VT1708S", + { .id = 0x11060397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11061397, .name = "VIA VT1708S", + { .id = 0x11061397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11062397, .name = "VIA VT1708S", + { .id = 0x11062397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11063397, .name = "VIA VT1708S", + { .id = 0x11063397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11064397, .name = "VIA VT1708S", + { .id = 0x11064397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11065397, .name = "VIA VT1708S", + { .id = 0x11065397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11066397, .name = "VIA VT1708S", + { .id = 0x11066397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11067397, .name = "VIA VT1708S", + { .id = 0x11067397, .name = "VT1708S", .patch = patch_vt1708S}, - { .id = 0x11060398, .name = "VIA VT1702", + { .id = 0x11060398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11061398, .name = "VIA VT1702", + { .id = 0x11061398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11062398, .name = "VIA VT1702", + { .id = 0x11062398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11063398, .name = "VIA VT1702", + { .id = 0x11063398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11064398, .name = "VIA VT1702", + { .id = 0x11064398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11065398, .name = "VIA VT1702", + { .id = 0x11065398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11066398, .name = "VIA VT1702", + { .id = 0x11066398, .name = "VT1702", .patch = patch_vt1702}, - { .id = 0x11067398, .name = "VIA VT1702", + { .id = 0x11067398, .name = "VT1702", .patch = patch_vt1702}, {} /* terminator */ }; + +MODULE_ALIAS("snd-hda-codec-id:1106*"); + +static struct hda_codec_preset_list via_list = { + .preset = snd_hda_preset_via, + .owner = THIS_MODULE, +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VIA HD-audio codec"); + +static int __init patch_via_init(void) +{ + return snd_hda_add_codec_preset(&via_list); +} + +static void __exit patch_via_exit(void) +{ + snd_hda_delete_codec_preset(&via_list); +} + +module_init(patch_via_init) +module_exit(patch_via_exit) |